From 2f5169af352ffb5ce50e50eacf42c3d00a9faa95 Mon Sep 17 00:00:00 2001 From: Alex Hancock Date: Thu, 20 Aug 2026 21:51:53 +0000 Subject: [PATCH] add MCP conformance tests to goose CI (combines #10800 + #10801) (#10940) Signed-off-by: Jack Amadeo Co-authored-by: Jack Amadeo --- .github/workflows/mcp-conformance.yml | 91 ++++ Justfile | 27 + crates/goose-cli/Cargo.toml | 4 + .../src/bin/mcp_conformance_driver.rs | 132 +++++ crates/goose-cli/src/cli.rs | 224 ++++++++ .../src/scenario_tests/mock_client.rs | 7 +- .../expected-failures-2025-11-25-0.1.16.yaml | 13 + ...ed-failures-2025-11-25-0.2.0-alpha.10.yaml | 13 + ...ed-failures-2026-07-28-0.2.0-alpha.10.yaml | 13 + crates/goose/src/agents/agent.rs | 10 +- crates/goose/src/agents/extension_manager.rs | 501 ++++++++++++++++-- crates/goose/src/agents/mcp_client.rs | 150 +++++- crates/goose/src/agents/mod.rs | 4 +- .../agents/state_machine/tests/pipeline.rs | 2 + crates/goose/src/oauth/mod.rs | 278 ++++++++-- crates/goose/tests/mcp_integration_test.rs | 17 +- .../tests/mcp_replays/github-mcp-serverstdio | 12 +- ...14.4fastmcpruntests_fastmcp_test_server.py | 8 +- 18 files changed, 1380 insertions(+), 126 deletions(-) create mode 100644 .github/workflows/mcp-conformance.yml create mode 100644 crates/goose-cli/src/bin/mcp_conformance_driver.rs create mode 100644 crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.1.16.yaml create mode 100644 crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.2.0-alpha.10.yaml create mode 100644 crates/goose-cli/tests/mcp-conformance/expected-failures-2026-07-28-0.2.0-alpha.10.yaml diff --git a/.github/workflows/mcp-conformance.yml b/.github/workflows/mcp-conformance.yml new file mode 100644 index 0000000000..5ed9cfa41a --- /dev/null +++ b/.github/workflows/mcp-conformance.yml @@ -0,0 +1,91 @@ +name: MCP Conformance + +on: + push: + branches: + - main + pull_request: + branches: + - main + merge_group: + branches: + - main + workflow_dispatch: + +jobs: + build: + name: Build Conformance Binaries + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0 + + - name: Install Dependencies + run: | + sudo apt update -y + sudo apt install -y libdbus-1-dev libxcb1-dev + + - name: Cache Cargo artifacts + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + key: mcp-conformance + + - name: Build + run: | + source ./bin/activate-hermit + export CARGO_INCREMENTAL=0 + just mcp-conformance-build + + - name: Upload Conformance Binaries + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mcp-conformance-binaries + path: | + target/debug/goose + target/debug/mcp_conformance_driver + retention-days: 1 + if-no-files-found: error + + conformance: + name: Conformance ${{ matrix.spec-version }} / ${{ matrix.conformance-version }} + runs-on: ubuntu-latest + needs: build + strategy: + fail-fast: false + matrix: + include: + - spec-version: "2025-11-25" + conformance-version: "0.1.16" + baseline: "crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.1.16.yaml" + - spec-version: "2025-11-25" + conformance-version: "0.2.0-alpha.10" + baseline: "crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.2.0-alpha.10.yaml" + - spec-version: "2026-07-28" + conformance-version: "0.2.0-alpha.10" + baseline: "crates/goose-cli/tests/mcp-conformance/expected-failures-2026-07-28-0.2.0-alpha.10.yaml" + steps: + - name: Checkout Code + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Install Dependencies + run: | + sudo apt update -y + sudo apt install -y libdbus-1-dev libxcb1-dev + + - name: Download Conformance Binaries + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: mcp-conformance-binaries + path: target/debug + + - name: Restore Executable Permissions + run: chmod +x target/debug/goose target/debug/mcp_conformance_driver + + - name: Run Conformance Suite + env: + GOOSE_DISABLE_KEYRING: "1" + run: | + source ./bin/activate-hermit + just mcp-conformance "${{ matrix.spec-version }}" all "${{ matrix.conformance-version }}" false "${{ matrix.baseline }}" diff --git a/Justfile b/Justfile index f140f5bb15..eadd07beda 100644 --- a/Justfile +++ b/Justfile @@ -413,6 +413,33 @@ win-total-rls *allparam: just win-bld-rls{{allparam}} just win-run-rls +# Build the binaries the MCP conformance driver needs. +mcp-conformance-build: + cargo build -p goose-cli --bin goose --bin mcp_conformance_driver + +# suite: all, core, extensions, backcompat, auth, metadata, draft, sep-835 +# build: "false" reuses the existing target/debug binaries instead of rebuilding +# Example: just mcp-conformance +# Example: just mcp-conformance 2025-11-25 auth +# Example: just mcp-conformance 2025-11-25 auth 0.2.0-alpha.10 +# Example: just mcp-conformance 2025-11-25 auth 0.2.0-alpha.10 false +# Example: just mcp-conformance 2025-11-25 all 0.2.0-alpha.10 true crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.2.0-alpha.10.yaml +[doc("Run an MCP client conformance suite against Goose.")] +mcp-conformance version="2025-11-25" suite="all" conformance_version="0.2.0-alpha.10" build="true" baseline="": + #!/usr/bin/env bash + set -euo pipefail + if [ "{{build}}" = "true" ]; then + just mcp-conformance-build + elif [ ! -x target/debug/mcp_conformance_driver ]; then + echo "target/debug/mcp_conformance_driver not found; run 'just mcp-conformance-build' first" >&2 + exit 1 + fi + baseline_args=() + if [ -n "{{baseline}}" ]; then + baseline_args=(--expected-failures "{{baseline}}") + fi + GOOSE_DISABLE_KEYRING=1 npx -y @modelcontextprotocol/conformance@{{conformance_version}} client --command "target/debug/mcp_conformance_driver" --spec-version "{{version}}" --suite "{{suite}}" ${baseline_args[@]+"${baseline_args[@]}"} + build-test-tools: cargo build -p goose-test diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index b3f6cdcd49..cc9c5f1c45 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -19,6 +19,10 @@ path = "src/main.rs" name = "generate_manpages" path = "src/bin/generate_manpages.rs" +[[bin]] +name = "mcp_conformance_driver" +path = "src/bin/mcp_conformance_driver.rs" + [dependencies] clap_mangen = { version = "0.3", default-features = false } goose = { path = "../goose", default-features = false } diff --git a/crates/goose-cli/src/bin/mcp_conformance_driver.rs b/crates/goose-cli/src/bin/mcp_conformance_driver.rs new file mode 100644 index 0000000000..b6abc5559e --- /dev/null +++ b/crates/goose-cli/src/bin/mcp_conformance_driver.rs @@ -0,0 +1,132 @@ +use std::io::Write; +use std::process::{Command, Stdio}; + +use serde_json::{json, Map, Value}; + +fn script_for_scenario(scenario: Option<&str>) -> Value { + let context: Map = std::env::var("MCP_CONFORMANCE_CONTEXT") + .ok() + .and_then(|raw| serde_json::from_str(&raw).ok()) + .unwrap_or_default(); + + let mut script = match scenario { + Some("tools_call") => json!({ + "steps": [{ "action": "callTool", "name": "add_numbers", "arguments": { "a": 2, "b": 3 } }], + }), + Some("elicitation-sep1034-client-defaults") => json!({ + "steps": [{ "action": "callTool", "name": "test_client_elicitation_defaults", "arguments": {} }], + "elicitation": { "action": "acceptSchemaDefaults" }, + }), + Some("auth/scope-step-up") => json!({ + "steps": [{ "action": "callTool", "name": "test-tool", "arguments": {} }], + }), + Some("sse-retry") => json!({ + "steps": [{ "action": "callTool", "name": "test_reconnection", "arguments": {} }], + }), + Some("auth/basic-cimd") => json!({ + "steps": [{ "action": "listTools" }], + "oauth": { "clientMetadataUrl": "https://conformance-test.local/client-metadata.json" }, + }), + Some("auth/pre-registration") => json!({ + "steps": [{ "action": "listTools" }], + "oauth": { "clientId": context.get("client_id"), "clientSecret": context.get("client_secret") }, + }), + Some("sep-2322-client-request-state") => json!({ + "steps": [ + { "action": "callTool", "name": "test_mrtr_echo_state", "arguments": {} }, + { "action": "callTool", "name": "test_mrtr_no_state", "arguments": {} }, + { "action": "callTool", "name": "test_mrtr_unrelated", "arguments": {} }, + { "action": "callTool", "name": "test_mrtr_no_result_type", "arguments": {} }, + ], + "elicitation": { "action": "accept", "content": { "confirmed": true } }, + }), + Some("http-custom-headers") => { + let steps: Vec = context + .get("toolCalls") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default() + .into_iter() + .map(|call| { + let mut step = Map::new(); + step.insert("action".to_string(), json!("callTool")); + if let Value::Object(call) = call { + step.extend(call); + } + Value::Object(step) + }) + .collect(); + json!({ "steps": steps }) + } + Some("http-invalid-tool-headers") => json!({ + "steps": [{ "action": "callTool", "name": "valid_tool", "arguments": {} }], + }), + Some("http-standard-headers") => json!({ + "steps": [ + { "action": "listTools" }, + { "action": "callTool", "name": "test_headers", "arguments": {} }, + { "action": "listPrompts" }, + { "action": "getPrompt", "name": "test_prompt", "arguments": {} }, + { "action": "listResources" }, + { "action": "readResource", "uri": "file:///path/to/file%20name.txt" }, + ], + }), + _ => json!({ + "steps": [ + { "action": "listTools" }, + { "action": "listPrompts" }, + { "action": "listResources" }, + ], + }), + }; + + // Runner 0.1.16 does not set MCP_CONFORMANCE_PROTOCOL_VERSION; default to + // the 2025-11-25 spec version those scenarios expect. + let protocol_version = std::env::var("MCP_CONFORMANCE_PROTOCOL_VERSION") + .unwrap_or_else(|_| "2025-11-25".to_string()); + { + script["protocolVersion"] = json!(protocol_version); + } + script +} + +fn main() { + let args: Vec = std::env::args().skip(1).collect(); + let [target] = args.as_slice() else { + eprintln!("usage: mcp_conformance_driver "); + std::process::exit(2); + }; + + let scenario = std::env::var("MCP_CONFORMANCE_SCENARIO").ok(); + let script = script_for_scenario(scenario.as_deref()); + + let goose = std::env::var("GOOSE_BIN").unwrap_or_else(|_| "target/debug/goose".to_string()); + let path_root = tempfile::Builder::new() + .prefix("goose-mcp-conformance-") + .tempdir() + .unwrap_or_else(|err| { + eprintln!("failed to create temporary GOOSE_PATH_ROOT: {err}"); + std::process::exit(1); + }); + let mut child = Command::new(&goose) + .args(["mcp-probe", target, "--script", "-"]) + .env("GOOSE_OAUTH_AUTOMATIC_CALLBACK", "1") + .env("GOOSE_DISABLE_KEYRING", "1") + .env("GOOSE_PATH_ROOT", path_root.path()) + .stdin(Stdio::piped()) + .spawn() + .unwrap_or_else(|err| { + eprintln!("failed to spawn {goose}: {err}"); + std::process::exit(1); + }); + + child + .stdin + .take() + .expect("stdin was piped") + .write_all(script.to_string().as_bytes()) + .expect("write probe script to goose stdin"); + + let status = child.wait().expect("wait for goose"); + std::process::exit(status.code().unwrap_or(1)); +} diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index a144512cfd..e16ce315a7 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -1194,6 +1194,23 @@ enum Command { #[arg(help = "Path to the bundled-extensions.json file")] file: PathBuf, }, + + #[command( + name = "mcp-probe", + about = "Start a Goose MCP session without an LLM and inspect a stdio MCP server", + hide = true + )] + McpProbe { + #[arg(help = "Stdio MCP server command to inspect")] + extension: String, + + #[arg( + long, + value_name = "PATH|-", + help = "JSON probe script; use - for stdin" + )] + script: Option, + }, } #[cfg(feature = "local-inference")] @@ -1358,10 +1375,216 @@ fn get_command_name(command: &Option) -> &'static str { Some(Command::Completion { .. }) => "completion", Some(Command::Review { .. }) => "review", Some(Command::ValidateExtensions { .. }) => "validate-extensions", + Some(Command::McpProbe { .. }) => "mcp-probe", None => "default_session", } } +#[derive(serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct McpProbeScript { + #[serde(default)] + steps: Vec, + elicitation: Option, + #[serde(default)] + oauth: goose::oauth::OAuthFlowConfig, + protocol_version: Option, +} + +#[derive(serde::Deserialize)] +#[serde(tag = "action", rename_all = "camelCase")] +enum McpProbeStep { + ListTools, + ListPrompts, + ListResources, + CallTool { + name: String, + #[serde(default)] + arguments: serde_json::Map, + }, +} + +#[derive(Clone, serde::Deserialize)] +#[serde(tag = "action", rename_all = "camelCase")] +enum McpProbeElicitation { + Accept { content: serde_json::Value }, + AcceptSchemaDefaults, + Decline, + Cancel, +} + +async fn handle_mcp_probe(extension_command: String, script_path: Option) -> Result<()> { + use goose::agents::{Agent, AgentConfig, ToolCallContext}; + use goose::config::ExtensionConfig; + use rmcp::model::{ElicitRequestParams, ElicitResult, ElicitationAction}; + use tokio_util::sync::CancellationToken; + + let script = if let Some(path) = script_path { + let json = if path == "-" { + let mut json = String::new(); + std::io::stdin().read_to_string(&mut json)?; + json + } else { + std::fs::read_to_string(path)? + }; + serde_json::from_str::(&json)? + } else { + McpProbeScript { + steps: vec![ + McpProbeStep::ListTools, + McpProbeStep::ListPrompts, + McpProbeStep::ListResources, + ], + elicitation: None, + oauth: goose::oauth::OAuthFlowConfig::default(), + protocol_version: None, + } + }; + + let mut extension = if url::Url::parse(&extension_command) + .is_ok_and(|url| matches!(url.scheme(), "http" | "https")) + { + crate::session::CliSession::parse_streamable_http_extension( + &extension_command, + goose::config::DEFAULT_EXTENSION_TIMEOUT, + ) + } else { + crate::session::CliSession::parse_stdio_extension(&extension_command)? + }; + match &mut extension { + ExtensionConfig::Stdio { name, .. } | ExtensionConfig::StreamableHttp { name, .. } => { + *name = "probe".to_string(); + } + _ => unreachable!("MCP probe only creates stdio or streamable HTTP extensions"), + } + + if let Some(client_id) = &script.oauth.client_id { + std::env::set_var("GOOSE_MCP_OAUTH_CLIENT_ID", client_id); + } + if let Some(client_secret) = &script.oauth.client_secret { + std::env::set_var("GOOSE_MCP_OAUTH_CLIENT_SECRET", client_secret); + } + if let Some(client_metadata_url) = &script.oauth.client_metadata_url { + std::env::set_var("GOOSE_MCP_OAUTH_CLIENT_METADATA_URL", client_metadata_url); + } + + let config = goose::config::Config::global(); + let mut agent_config = AgentConfig::new( + std::sync::Arc::new(SessionManager::instance()), + goose::config::permission::PermissionManager::instance(), + None, + config.get_goose_mode().unwrap_or_default(), + true, + GoosePlatform::GooseCli, + ); + if let Some(protocol_version) = script.protocol_version.as_deref() { + agent_config.mcp_protocol_version = Some(serde_json::from_value( + serde_json::Value::String(protocol_version.to_string()), + )?); + } + if let Some(action) = script.elicitation.clone() { + agent_config.elicitation_handler = + Some(std::sync::Arc::new(move |request| match &action { + McpProbeElicitation::Accept { content } => { + ElicitResult::new(ElicitationAction::Accept).with_content(content.clone()) + } + McpProbeElicitation::AcceptSchemaDefaults => { + let content = match request { + ElicitRequestParams::FormElicitationParams { + requested_schema, .. + } => serde_json::to_value(requested_schema) + .ok() + .and_then(|schema| schema.get("properties").cloned()) + .and_then(|properties| properties.as_object().cloned()) + .map(|properties| { + properties + .into_iter() + .filter_map(|(name, schema)| { + schema.get("default").cloned().map(|value| (name, value)) + }) + .collect() + }) + .unwrap_or_default(), + _ => serde_json::Map::new(), + }; + ElicitResult::new(ElicitationAction::Accept) + .with_content(serde_json::Value::Object(content)) + } + McpProbeElicitation::Decline => ElicitResult::new(ElicitationAction::Decline), + McpProbeElicitation::Cancel => ElicitResult::new(ElicitationAction::Cancel), + })); + } + let agent = Agent::with_config(agent_config); + let session = agent + .config + .session_manager + .create_session( + std::env::current_dir()?, + "MCP Probe".to_string(), + goose::session::session_manager::SessionType::Hidden, + agent.config.goose_mode, + ) + .await?; + let session_id = session.id.as_str(); + agent.add_extension(extension, session_id).await?; + + let mut results = Vec::new(); + for step in script.steps { + let result = match step { + McpProbeStep::ListTools => serde_json::json!({ + "action": "listTools", + "result": agent.extension_manager.list_tools_from_extension( + session_id, + "probe", + CancellationToken::new(), + ).await?, + }), + McpProbeStep::ListPrompts => serde_json::json!({ + "action": "listPrompts", + "result": agent.extension_manager.list_prompts_from_extension( + session_id, + "probe", + CancellationToken::new(), + ).await?, + }), + McpProbeStep::ListResources => serde_json::json!({ + "action": "listResources", + "result": agent.extension_manager.list_resources_result_from_extension( + session_id, + "probe", + CancellationToken::new(), + ).await?, + }), + McpProbeStep::CallTool { name, arguments } => { + let scoped_name = format!("probe__{name}"); + let ctx = ToolCallContext::new( + session_id.to_string(), + Some(std::env::current_dir()?), + Some("mcp-probe-tool-call".to_string()), + ); + let result = agent + .extension_manager + .dispatch_tool_call( + &ctx, + rmcp::model::CallToolRequestParams::new(scoped_name) + .with_arguments(arguments), + CancellationToken::new(), + ) + .await? + .result + .await?; + serde_json::json!({ "action": "callTool", "name": name, "result": result }) + } + }; + results.push(result); + } + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ "results": results }))? + ); + Ok(()) +} + async fn handle_mcp_command(server: McpCommand) -> Result<()> { let name = server.name(); let _ = crate::logging::setup_logging(Some(&format!("mcp-{name}"))); @@ -2385,6 +2608,7 @@ pub async fn cli() -> anyhow::Result<()> { } } } + Some(Command::McpProbe { extension, script }) => handle_mcp_probe(extension, script).await, None => handle_default_session().await, } } diff --git a/crates/goose-cli/src/scenario_tests/mock_client.rs b/crates/goose-cli/src/scenario_tests/mock_client.rs index d419449fa8..3030cf1cbe 100644 --- a/crates/goose-cli/src/scenario_tests/mock_client.rs +++ b/crates/goose-cli/src/scenario_tests/mock_client.rs @@ -118,12 +118,7 @@ impl McpClientTrait for MockClient { _next_cursor: Option, _cancel_token: CancellationToken, ) -> Result { - Ok(ListPromptsResult { - prompts: vec![], - next_cursor: None, - meta: None, - ..Default::default() - }) + Ok(ListPromptsResult::with_all_items(vec![])) } async fn get_prompt( diff --git a/crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.1.16.yaml b/crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.1.16.yaml new file mode 100644 index 0000000000..b40738f01e --- /dev/null +++ b/crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.1.16.yaml @@ -0,0 +1,13 @@ +# MCP conformance baseline: known-failing client scenarios. +# +# Spec version: 2025-11-25 +# Conformance version: 0.1.16 +# +# Scenarios listed here are allowed to fail (or emit warnings) without failing +# CI. The conformance runner will error if any of these scenarios start passing +# (stale entry) or if a scenario not listed here fails (unexpected failure), so +# keep this list in sync as behavior changes. +# +# All scenarios currently pass; keep this file so CI flags any regression as an +# unexpected failure. +client: [] diff --git a/crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.2.0-alpha.10.yaml b/crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.2.0-alpha.10.yaml new file mode 100644 index 0000000000..c60d88b4c6 --- /dev/null +++ b/crates/goose-cli/tests/mcp-conformance/expected-failures-2025-11-25-0.2.0-alpha.10.yaml @@ -0,0 +1,13 @@ +# MCP conformance baseline: known-failing client scenarios. +# +# Spec version: 2025-11-25 +# Conformance version: 0.2.0-alpha.10 +# +# Scenarios listed here are allowed to fail (or emit warnings) without failing +# CI. The conformance runner will error if any of these scenarios start passing +# (stale entry) or if a scenario not listed here fails (unexpected failure), so +# keep this list in sync as behavior changes. +# +# All scenarios currently pass; keep this file so CI flags any regression as an +# unexpected failure. +client: [] diff --git a/crates/goose-cli/tests/mcp-conformance/expected-failures-2026-07-28-0.2.0-alpha.10.yaml b/crates/goose-cli/tests/mcp-conformance/expected-failures-2026-07-28-0.2.0-alpha.10.yaml new file mode 100644 index 0000000000..979b55cd72 --- /dev/null +++ b/crates/goose-cli/tests/mcp-conformance/expected-failures-2026-07-28-0.2.0-alpha.10.yaml @@ -0,0 +1,13 @@ +# MCP conformance baseline: known-failing client scenarios. +# +# Spec version: 2026-07-28 +# Conformance version: 0.2.0-alpha.10 +# +# Scenarios listed here are allowed to fail (or emit warnings) without failing +# CI. The conformance runner will error if any of these scenarios start passing +# (stale entry) or if a scenario not listed here fails (unexpected failure), so +# keep this list in sync as behavior changes. +# +# All scenarios currently pass; keep this file so CI flags any regression as an +# unexpected failure. +client: [] diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index 64628a4a78..5d4f9627ce 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -73,7 +73,7 @@ use goose_providers::thinking::{ThinkingEffort, ThinkingEffortSupport}; use regex::Regex; use rmcp::model::{ CallToolRequestParams, CallToolResult, ContentBlock, ElicitationAction, ErrorCode, ErrorData, - GetPromptResult, Prompt, Tool, + GetPromptResult, Prompt, ProtocolVersion, Tool, }; use serde_json::Value; use tokio::sync::{mpsc, Mutex}; @@ -93,6 +93,8 @@ fn provider_creation_error(error: anyhow::Error, context: impl fmt::Display) -> error.context(message) } +pub const MCP_PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion::V_2025_11_25; + fn normalize_legacy_provider_thinking_effort( mut model_config: goose_providers::model::ModelConfig, effort_support: &ThinkingEffortSupport, @@ -222,6 +224,8 @@ pub struct AgentConfig { pub disable_session_naming: bool, pub goose_platform: GoosePlatform, pub mcp_host_info: Option, + pub elicitation_handler: Option, + pub mcp_protocol_version: Option, pub session_name_update_tx: Option>, pub use_login_shell_path: Option, pub is_subagent: bool, @@ -244,6 +248,8 @@ impl AgentConfig { disable_session_naming, goose_platform, mcp_host_info: None, + elicitation_handler: None, + mcp_protocol_version: Some(MCP_PROTOCOL_VERSION), session_name_update_tx: None, use_login_shell_path: None, is_subagent: false, @@ -413,6 +419,8 @@ impl Agent { let capabilities = ExtensionManagerCapabilities { mcpui, host_info: explicit_mcp_host_info.clone(), + elicitation_handler: config.elicitation_handler.clone(), + protocol_version: config.mcp_protocol_version.clone(), }; let client_name = explicit_mcp_host_info .as_ref() diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 1b8198476f..c9c6e5c28c 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -42,12 +42,15 @@ use crate::builtin_extension::get_builtin_extension; use crate::config::extensions::name_to_key; use crate::config::search_path::SearchPaths; use crate::config::{get_all_extensions, Config}; -use crate::oauth::{oauth_flow, GooseCredentialStore, StaticOAuthClientConfig}; +use crate::oauth::{ + oauth_flow, oauth_flow_with_challenge, GooseCredentialStore, StaticOAuthClientConfig, +}; use crate::prompt_template; use crate::subprocess::spawn_long_lived_mcp_subprocess; use rmcp::model::{ CallToolRequestParams, CallToolResult, ContentBlock, ErrorCode, ErrorData, GetPromptResult, - MetaObject, Prompt, Resource, ResourceContents, ServerInfo, ServerNotification, Tool, + ListResourcesResult, ListToolsResult, MetaObject, Prompt, Resource, ResourceContents, + ServerInfo, ServerNotification, Tool, }; use rmcp::transport::auth::{AuthClient, CredentialStore}; use schemars::_private::NoSerialize; @@ -167,6 +170,8 @@ impl Extension { pub struct ExtensionManagerCapabilities { pub mcpui: bool, pub host_info: Option, + pub elicitation_handler: Option, + pub protocol_version: Option, } #[derive(Debug, Clone, serde::Serialize)] @@ -492,8 +497,12 @@ fn is_oauth_auth_failure(err: &ClientInitializeError) -> bool { if let Some(http_err) = error.downcast_ref::>() { return match http_err { - StreamableHttpError::AuthRequired(_) => true, - StreamableHttpError::UnexpectedServerResponse(body) => body.starts_with("HTTP 401"), + StreamableHttpError::AuthRequired(_) | StreamableHttpError::InsufficientScope(_) => { + true + } + StreamableHttpError::UnexpectedServerResponse(body) => { + body.starts_with("HTTP 401") || body.starts_with("HTTP 403") + } _ => false, }; } @@ -504,21 +513,81 @@ fn is_oauth_auth_failure(err: &ClientInitializeError) -> bool { ) { return match http_err { - StreamableHttpError::AuthRequired(_) => true, - StreamableHttpError::UnexpectedServerResponse(body) => body.starts_with("HTTP 401"), + StreamableHttpError::AuthRequired(_) | StreamableHttpError::InsufficientScope(_) => { + true + } + StreamableHttpError::UnexpectedServerResponse(body) => { + body.starts_with("HTTP 401") || body.starts_with("HTTP 403") + } _ => false, }; } - error - .to_string() - .contains("unexpected server response: HTTP 401") + let message = error.to_string(); + message.contains("unexpected server response: HTTP 401") + || message.contains("unexpected server response: HTTP 403") + || message.contains("Auth required") + || message.contains("Authorization required") } fn should_attempt_oauth_fallback(res: &Result) -> bool { res.as_ref().err().is_some_and(is_oauth_auth_failure) } +/// Extract the `WWW-Authenticate` challenge from a failed initialization, so +/// OAuth discovery can be seeded from the server's 401/403 response instead of +/// probing well-known locations. +fn auth_challenge_from_error(err: &ClientInitializeError) -> Option { + let ClientInitializeError::TransportError { + error: DynamicTransportError { error, .. }, + .. + } = err + else { + return None; + }; + + if let Some(http_err) = error.downcast_ref::>() { + return http_err.auth_challenge().map(str::to_string); + } + + #[cfg(unix)] + if let Some(http_err) = error + .downcast_ref::>( + ) + { + return http_err.auth_challenge().map(str::to_string); + } + + None +} + +fn auth_challenge_from_result(res: &Result) -> Option { + res.as_ref().err().and_then(auth_challenge_from_error) +} + +/// Extract the `WWW-Authenticate` challenge from a post-initialization request +/// failure (401 auth required or 403 insufficient scope), so a step-up +/// authorization can be started reactively. +fn auth_challenge_from_service_error(err: &ServiceError) -> Option { + let ServiceError::TransportSend(DynamicTransportError { error, .. }) = err else { + return None; + }; + + if let Some(http_err) = error.downcast_ref::>() { + return http_err.auth_challenge().map(str::to_string); + } + + #[cfg(unix)] + if let Some(http_err) = error + .downcast_ref::>( + ) + { + return http_err.auth_challenge().map(str::to_string); + } + + None +} + async fn clear_credentials_on_post_refresh_auth_failure( credential_store: &dyn CredentialStore, name: &str, @@ -528,7 +597,13 @@ async fn clear_credentials_on_post_refresh_auth_failure( return false; }; - if !is_oauth_auth_failure(err) { + if !is_oauth_auth_failure(err) + || auth_challenge_from_error(err).is_some_and(|challenge| { + challenge + .to_ascii_lowercase() + .contains("insufficient_scope") + }) + { return false; } @@ -698,7 +773,7 @@ async fn connect_with_auth( capabilities: GooseMcpClientCapabilities, roots_dir: &std::path::Path, extension_manager: Weak, -) -> ExtensionResult> { +) -> ExtensionResult { let mut auth_headers = HeaderMap::new(); auth_headers.insert(reqwest::header::USER_AGENT, GOOSE_USER_AGENT); for (key, value) in headers { @@ -724,19 +799,307 @@ async fn connect_with_auth( auth_client, StreamableHttpClientTransportConfig::with_uri(uri), ); - Ok(Box::new( - McpClient::connect( - transport, - timeout, - provider, - client_name, - capabilities, - roots_dir.to_path_buf(), - action_required, - extension_manager, + Ok(McpClient::connect( + transport, + timeout, + provider, + client_name, + capabilities, + roots_dir.to_path_buf(), + action_required, + extension_manager, + ) + .await?) +} + +/// Connection parameters needed to re-establish an authorized streamable HTTP +/// client after a post-initialization auth challenge (401/403). +#[derive(Clone)] +struct StreamableHttpConnectParams { + uri: String, + name: String, + timeout: Duration, + headers: HashMap, + provider: SharedProvider, + client_name: String, + capabilities: GooseMcpClientCapabilities, + roots_dir: PathBuf, + action_required: Arc, + extension_manager: Weak, + static_oauth_client: Option, +} + +/// Wraps a streamable HTTP `McpClient` and handles step-up authorization: +/// when a request fails with a 401/403 carrying a `WWW-Authenticate` +/// challenge after initialization succeeded, re-authorize using the challenge +/// (requesting the union of scopes), reconnect, and retry the request once. +struct OAuthStepUpClient { + inner: tokio::sync::RwLock, + server_info: Option, + params: tokio::sync::RwLock, + step_up_lock: tokio::sync::Mutex<()>, + notification_subscribers: Arc>>>, +} + +impl OAuthStepUpClient { + async fn new(inner: McpClient, params: StreamableHttpConnectParams) -> Self { + let server_info = inner.get_info().cloned(); + let notification_subscribers = Arc::new(Mutex::new(Vec::new())); + Self::forward_notifications(&inner, notification_subscribers.clone()).await; + Self { + inner: tokio::sync::RwLock::new(inner), + server_info, + params: tokio::sync::RwLock::new(params), + step_up_lock: tokio::sync::Mutex::new(()), + notification_subscribers, + } + } + + async fn forward_notifications( + client: &McpClient, + subscribers: Arc>>>, + ) { + let mut receiver = client.subscribe().await; + tokio::spawn(async move { + while let Some(notification) = receiver.recv().await { + let mut subscribers = subscribers.lock().await; + subscribers.retain(|subscriber| subscriber.try_send(notification.clone()).is_ok()); + } + }); + } + + async fn step_up_reconnect( + &self, + challenge: String, + ) -> Result<(), crate::agents::mcp_client::Error> { + let params = self.params.read().await; + let auth_manager = oauth_flow_with_challenge( + ¶ms.uri, + ¶ms.name, + params.static_oauth_client.as_ref(), + Some(challenge), ) - .await?, - )) + .await + .map_err(|e| { + crate::agents::mcp_client::Error::McpError(ErrorData::new( + ErrorCode::INTERNAL_ERROR, + format!("step-up authorization failed: {e}"), + None, + )) + })?; + let client = connect_with_auth( + auth_manager, + params.action_required.clone(), + ¶ms.uri, + params.timeout, + ¶ms.headers, + params.provider.clone(), + params.client_name.clone(), + params.capabilities.clone(), + ¶ms.roots_dir, + params.extension_manager.clone(), + ) + .await + .map_err(|e| { + crate::agents::mcp_client::Error::McpError(ErrorData::new( + ErrorCode::INTERNAL_ERROR, + format!("reconnect after step-up authorization failed: {e}"), + None, + )) + })?; + Self::forward_notifications(&client, self.notification_subscribers.clone()).await; + *self.inner.write().await = client; + Ok(()) + } + + /// Run `op` against the current client; on an auth challenge, re-authorize + /// and retry once. + async fn with_step_up_retry(&self, op: F) -> Result + where + F: for<'a> Fn( + &'a McpClient, + ) -> Pin< + Box< + dyn std::future::Future> + + Send + + 'a, + >, + >, + { + let first = { + let client = self.inner.read().await; + op(&client).await + }; + match first { + Err(err) => { + if let Some(challenge) = auth_challenge_from_service_error(&err) { + let _step_up_guard = self.step_up_lock.lock().await; + let retry = { + let client = self.inner.read().await; + op(&client).await + }; + match retry { + Ok(value) => Ok(value), + Err(retry_err) + if auth_challenge_from_service_error(&retry_err).is_some() => + { + self.step_up_reconnect(challenge).await?; + let client = self.inner.read().await; + op(&client).await + } + Err(retry_err) => Err(retry_err), + } + } else { + Err(err) + } + } + ok => ok, + } + } +} + +#[async_trait::async_trait] +impl McpClientTrait for OAuthStepUpClient { + async fn list_tools( + &self, + session_id: &str, + next_cursor: Option, + cancel_token: CancellationToken, + ) -> Result { + let session_id = session_id.to_string(); + self.with_step_up_retry(move |client| { + let session_id = session_id.clone(); + let next_cursor = next_cursor.clone(); + let cancel_token = cancel_token.clone(); + Box::pin(async move { + client + .list_tools(&session_id, next_cursor, cancel_token) + .await + }) + }) + .await + } + + async fn call_tool( + &self, + ctx: &ToolCallContext, + name: &str, + arguments: Option, + cancel_token: CancellationToken, + ) -> Result { + let ctx = ctx.clone(); + let name = name.to_string(); + self.with_step_up_retry(move |client| { + let ctx = ctx.clone(); + let name = name.clone(); + let arguments = arguments.clone(); + let cancel_token = cancel_token.clone(); + Box::pin(async move { client.call_tool(&ctx, &name, arguments, cancel_token).await }) + }) + .await + } + + fn get_info(&self) -> Option<&ServerInfo> { + self.server_info.as_ref() + } + + async fn list_resources( + &self, + session_id: &str, + next_cursor: Option, + cancel_token: CancellationToken, + ) -> Result { + let session_id = session_id.to_string(); + self.with_step_up_retry(move |client| { + let session_id = session_id.clone(); + let next_cursor = next_cursor.clone(); + let cancel_token = cancel_token.clone(); + Box::pin(async move { + client + .list_resources(&session_id, next_cursor, cancel_token) + .await + }) + }) + .await + } + + async fn read_resource( + &self, + session_id: &str, + uri: &str, + cancel_token: CancellationToken, + ) -> Result { + let session_id = session_id.to_string(); + let uri = uri.to_string(); + self.with_step_up_retry(move |client| { + let session_id = session_id.clone(); + let uri = uri.clone(); + let cancel_token = cancel_token.clone(); + Box::pin(async move { client.read_resource(&session_id, &uri, cancel_token).await }) + }) + .await + } + + async fn list_prompts( + &self, + session_id: &str, + next_cursor: Option, + cancel_token: CancellationToken, + ) -> Result { + let session_id = session_id.to_string(); + self.with_step_up_retry(move |client| { + let session_id = session_id.clone(); + let next_cursor = next_cursor.clone(); + let cancel_token = cancel_token.clone(); + Box::pin(async move { + client + .list_prompts(&session_id, next_cursor, cancel_token) + .await + }) + }) + .await + } + + async fn get_prompt( + &self, + session_id: &str, + name: &str, + arguments: Value, + cancel_token: CancellationToken, + ) -> Result { + let session_id = session_id.to_string(); + let name = name.to_string(); + self.with_step_up_retry(move |client| { + let session_id = session_id.clone(); + let name = name.clone(); + let arguments = arguments.clone(); + let cancel_token = cancel_token.clone(); + Box::pin(async move { + client + .get_prompt(&session_id, &name, arguments, cancel_token) + .await + }) + }) + .await + } + + async fn subscribe(&self) -> tokio::sync::mpsc::Receiver { + let (sender, receiver) = mpsc::channel(32); + self.notification_subscribers.lock().await.push(sender); + receiver + } + + async fn get_moim(&self, session_id: &str) -> Option { + self.inner.read().await.get_moim(session_id).await + } + + async fn update_working_dir( + &self, + new_dir: PathBuf, + ) -> Result<(), crate::agents::mcp_client::Error> { + self.params.write().await.roots_dir = new_dir.clone(); + self.inner.read().await.update_working_dir(new_dir).await + } } #[allow(clippy::too_many_arguments)] @@ -810,6 +1173,20 @@ async fn create_streamable_http_client( StreamableHttpClientTransportConfig::with_uri(uri), ); + let connect_params = StreamableHttpConnectParams { + uri: uri.to_string(), + name: name.to_string(), + timeout: timeout_duration, + headers: headers.clone(), + provider: provider.clone(), + client_name: client_name.clone(), + capabilities: capabilities.clone(), + roots_dir: roots_dir.to_path_buf(), + action_required: action_required.clone(), + extension_manager: extension_manager.clone(), + static_oauth_client: static_oauth_client.clone(), + }; + // If we have stored OAuth credentials, try refreshing and connecting directly. // This avoids the unnecessary 401 → browser re-auth cycle on every new session. if credential_store.load().await.is_ok_and(|c| c.is_some()) { @@ -848,10 +1225,14 @@ async fn create_streamable_http_client( name ); } else { - return auth_result; + return Ok(Box::new( + OAuthStepUpClient::new(auth_result?, connect_params).await, + )); } } else { - return auth_result; + return Ok(Box::new( + OAuthStepUpClient::new(auth_result?, connect_params).await, + )); } } Err(e) => { @@ -876,15 +1257,17 @@ async fn create_streamable_http_client( .await; if should_attempt_oauth_fallback(&client_res) { - match oauth_flow( + let challenge = auth_challenge_from_result(&client_res); + match oauth_flow_with_challenge( &uri.to_string(), &name.to_string(), static_oauth_client.as_ref(), + challenge, ) .await { Ok(auth_manager) => { - connect_with_auth( + let client = connect_with_auth( auth_manager, action_required, uri, @@ -896,7 +1279,10 @@ async fn create_streamable_http_client( roots_dir, extension_manager, ) - .await + .await?; + Ok(Box::new( + OAuthStepUpClient::new(client, connect_params).await, + )) } Err(e) => { warn!( @@ -907,7 +1293,9 @@ async fn create_streamable_http_client( } } } else { - Ok(Box::new(client_res?)) + Ok(Box::new( + OAuthStepUpClient::new(client_res?, connect_params).await, + )) } } @@ -982,6 +1370,8 @@ impl ExtensionManager { GooseMcpClientCapabilities { mcpui: self.capabilities.mcpui, host_info: self.capabilities.host_info.clone(), + elicitation_handler: self.capabilities.elicitation_handler.clone(), + protocol_version: self.capabilities.protocol_version.clone(), } } @@ -1020,6 +1410,8 @@ impl ExtensionManager { ExtensionManagerCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }, false, ) @@ -1411,6 +1803,35 @@ impl ExtensionManager { Ok(self.filter_tools(&all_tools, extension_name.as_deref(), None)) } + pub async fn list_tools_from_extension( + &self, + session_id: &str, + extension_name: &str, + cancellation_token: CancellationToken, + ) -> Result { + let client = self + .get_server_client(extension_name) + .await + .ok_or_else(|| { + ErrorData::new( + ErrorCode::INVALID_PARAMS, + format!("Extension {} is not valid", extension_name), + None, + ) + })?; + + client + .list_tools(session_id, None, cancellation_token) + .await + .map_err(|e| { + ErrorData::new( + ErrorCode::INTERNAL_ERROR, + format!("Unable to list tools for {}, {:?}", extension_name, e), + None, + ) + }) + } + pub async fn get_prefixed_tools_excluding( &self, session_id: &str, @@ -1729,12 +2150,12 @@ impl ExtensionManager { Ok(ui_resources) } - async fn list_resources_from_extension( + pub async fn list_resources_result_from_extension( &self, session_id: &str, extension_name: &str, cancellation_token: CancellationToken, - ) -> Result, ErrorData> { + ) -> Result { let client = self .get_server_client(extension_name) .await @@ -1756,6 +2177,16 @@ impl ExtensionManager { None, ) }) + } + + async fn list_resources_from_extension( + &self, + session_id: &str, + extension_name: &str, + cancellation_token: CancellationToken, + ) -> Result, ErrorData> { + self.list_resources_result_from_extension(session_id, extension_name, cancellation_token) + .await .map(|lr| { let resource_list = lr .resources @@ -3684,6 +4115,8 @@ mod tests { let capabilities = GooseMcpClientCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }; let result = create_streamable_http_client( @@ -3722,6 +4155,8 @@ mod tests { let capabilities = GooseMcpClientCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }; let result = create_streamable_http_client( @@ -3769,6 +4204,8 @@ mod tests { let capabilities = GooseMcpClientCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }; // The MCP handshake will fail against the stub server. We only care that @@ -3855,6 +4292,8 @@ mod tests { let capabilities = GooseMcpClientCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }; // connect_with_auth will fail (mock server isn't an MCP server) but we diff --git a/crates/goose/src/agents/mcp_client.rs b/crates/goose/src/agents/mcp_client.rs index 052d71cc1b..acf92addfb 100644 --- a/crates/goose/src/agents/mcp_client.rs +++ b/crates/goose/src/agents/mcp_client.rs @@ -19,16 +19,16 @@ use rmcp::{ CallToolRequestParams, CallToolResult, CancelledNotificationParam, ClientCapabilities, ClientInfo, ClientRequest, GetPromptRequestParams, GetPromptResult, Implementation, InitializeRequestParams, InitializeResult, ListPromptsResult, ListResourcesResult, - ListToolsResult, Notification, PaginatedRequestParams, ReadResourceRequestParams, - ReadResourceResult, Request, RequestId, RequestOptionalParam, Role, ServerNotification, - ServerResult, + ListToolsResult, Notification, PaginatedRequestParams, ProtocolVersion, + ReadResourceRequestParams, ReadResourceResult, Request, RequestId, RequestOptionalParam, + Role, ServerNotification, ServerResult, }, service::{ - ClientInitializeError, PeerRequestOptions, RequestContext, RequestHandle, RunningService, - ServiceRole, + ClientInitializeError, ClientLifecycleMode, ClientServiceExt, PeerRequestOptions, + RequestContext, RequestHandle, RunningService, ServiceRole, }, transport::IntoTransport, - ClientHandler, ErrorData, Peer, RoleClient, ServiceError, ServiceExt, + ClientHandler, ErrorData, Peer, RoleClient, ServiceError, }; use serde_json::Value; use std::{ @@ -499,6 +499,10 @@ impl ClientHandler for GooseClient { request: ElicitRequestParams, context: RequestContext, ) -> Result { + if let Some(handler) = &self.capabilities.elicitation_handler { + return Ok(handler(&request)); + } + let session_id = self .resolve_session_id(&context.extensions) .await @@ -571,18 +575,40 @@ impl ClientHandler for GooseClient { .build(), self.resolved_client_info(), ) + .with_protocol_version( + self.capabilities + .protocol_version + .clone() + .unwrap_or_default(), + ) } } -#[derive(Debug, Clone)] +pub type ElicitationHandler = Arc ElicitResult + Send + Sync>; + +#[derive(Clone, Default)] pub struct GooseMcpClientCapabilities { pub mcpui: bool, pub host_info: Option, + pub elicitation_handler: Option, + pub protocol_version: Option, +} + +impl std::fmt::Debug for GooseMcpClientCapabilities { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter + .debug_struct("GooseMcpClientCapabilities") + .field("mcpui", &self.mcpui) + .field("host_info", &self.host_info) + .field("elicitation_handler", &self.elicitation_handler.is_some()) + .field("protocol_version", &self.protocol_version) + .finish() + } } /// The MCP client is the interface for MCP operations. pub struct McpClient { - client: Mutex>, + client: Mutex>>, notification_subscribers: Arc>>>, server_info: Option, timeout: std::time::Duration, @@ -648,7 +674,26 @@ impl McpClient { extension_manager, ); let client: rmcp::service::RunningService = - client.serve(transport).await?; + if let Some(protocol_version) = capabilities.protocol_version { + let lifecycle = if protocol_version >= ProtocolVersion::STANDARD_HEADERS { + ClientLifecycleMode::Discover { + preferred_versions: vec![protocol_version], + } + } else { + ClientLifecycleMode::Initialize + }; + client.serve_with_lifecycle(transport, lifecycle).await? + } else { + client + .serve_with_lifecycle( + transport, + ClientLifecycleMode::Auto { + preferred_versions: vec![ProtocolVersion::V_2026_07_28], + legacy_version: Some(ProtocolVersion::LATEST), + }, + ) + .await? + }; let server_info = client.peer_info().map(|info| { let mut initialize_result = InitializeResult::new(info.capabilities.clone()) .with_protocol_version(info.protocol_version.clone()); @@ -661,7 +706,7 @@ impl McpClient { }); Ok(Self { - client: Mutex::new(client), + client: Mutex::new(Arc::new(client)), notification_subscribers, server_info, timeout, @@ -788,14 +833,27 @@ impl McpClientTrait for McpClient { uri: &str, cancel_token: CancellationToken, ) -> Result { + let params = ReadResourceRequestParams::new(uri.to_string()); + let client = self.client.lock().await.clone(); + if client + .peer_info() + .is_some_and(|info| info.protocol_version == ProtocolVersion::V_2026_07_28) + { + client.service().set_session_id(session_id).await; + return tokio::select! { + result = client.read_resource(params) => result, + _ = tokio::time::sleep(self.timeout) => Err(ServiceError::Timeout { timeout: self.timeout }), + _ = cancel_token.cancelled() => Err(ServiceError::Cancelled { reason: None }), + }; + } + drop(client); + let res = self .send_request_with_context( session_id, None, None, - ClientRequest::ReadResourceRequest(Request::new(ReadResourceRequestParams::new( - uri.to_string(), - ))), + ClientRequest::ReadResourceRequest(Request::new(params)), cancel_token, ) .await?; @@ -841,6 +899,46 @@ impl McpClientTrait for McpClient { if let Some(args) = arguments { params = params.with_arguments(args); } + let protocol_version = { + let client = self.client.lock().await; + client.peer_info().map(|info| info.protocol_version.clone()) + }; + if protocol_version.as_ref() == Some(&ProtocolVersion::V_2026_07_28) { + let extensions = inject_session_context_into_extensions( + Extensions::new(), + Some(&ctx.session_id), + ctx.working_dir_str(), + ctx.tool_call_request_id.as_deref(), + ); + if let Some(meta) = extensions.get::() { + params.meta.get_or_insert_default().0 .0.extend( + meta.0 + .iter() + .map(|(key, value)| (key.clone(), value.clone())), + ); + } + let client = self.client.lock().await.clone(); + client.service().set_session_id(&ctx.session_id).await; + let _active_tool_call_guard = ctx + .tool_call_request_id + .as_deref() + .filter(|id| !id.is_empty()) + .map(|tool_call_request_id| { + client + .service() + .register_active_tool_call(&ctx.session_id, tool_call_request_id) + }); + return tokio::select! { + result = client.call_tool(params) => result, + _ = tokio::time::sleep(self.timeout) => { + Err(ServiceError::Timeout { timeout: self.timeout }) + } + _ = cancel_token.cancelled() => { + Err(ServiceError::Cancelled { reason: None }) + } + }; + } + let request = ClientRequest::CallToolRequest(Request::new(params)); let result = self @@ -898,6 +996,20 @@ impl McpClientTrait for McpClient { if let Some(args) = arguments { params = params.with_arguments(args); } + let client = self.client.lock().await.clone(); + if client + .peer_info() + .is_some_and(|info| info.protocol_version == ProtocolVersion::V_2026_07_28) + { + client.service().set_session_id(session_id).await; + return tokio::select! { + result = client.get_prompt(params) => result, + _ = tokio::time::sleep(self.timeout) => Err(ServiceError::Timeout { timeout: self.timeout }), + _ = cancel_token.cancelled() => Err(ServiceError::Cancelled { reason: None }), + }; + } + drop(client); + let res = self .send_request_with_context( session_id, @@ -1103,10 +1215,14 @@ mod tests { GoosePlatform::GooseDesktop => GooseMcpClientCapabilities { mcpui: true, host_info: None, + elicitation_handler: None, + protocol_version: None, }, GoosePlatform::GooseCli => GooseMcpClientCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }, }; @@ -1157,6 +1273,8 @@ mod tests { GooseMcpClientCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }, temp_dir.path().to_path_buf(), Arc::new(ActionRequiredManager::new()), @@ -1533,6 +1651,8 @@ mod tests { client_name: Some("goose2".to_string()), client_version: Some("0.1.0".to_string()), }), + elicitation_handler: None, + protocol_version: None, }, std::env::current_dir().unwrap_or_default(), Arc::new(ActionRequiredManager::new()), @@ -1566,6 +1686,8 @@ mod tests { client_name: Some("goose2".to_string()), client_version: Some("0.1.0".to_string()), }), + elicitation_handler: None, + protocol_version: None, }, std::env::current_dir().unwrap_or_default(), Arc::new(ActionRequiredManager::new()), @@ -1596,6 +1718,8 @@ mod tests { client_name: Some("goose2".to_string()), client_version: Some("0.1.0".to_string()), }), + elicitation_handler: None, + protocol_version: None, }, std::env::current_dir().unwrap_or_default(), Arc::new(ActionRequiredManager::new()), diff --git a/crates/goose/src/agents/mod.rs b/crates/goose/src/agents/mod.rs index 909b2899e7..5ac6f9d5d6 100644 --- a/crates/goose/src/agents/mod.rs +++ b/crates/goose/src/agents/mod.rs @@ -20,12 +20,12 @@ pub mod subagent_execution_tool; pub(crate) mod subagent_handler; pub(crate) mod subagent_task_config; mod tool_confirmation_router; -mod tool_execution; +pub mod tool_execution; mod tool_schema_normalize; pub mod types; pub mod validate_extensions; -pub use agent::{Agent, AgentConfig, ExtensionLoadResult, GoosePlatform}; +pub use agent::{Agent, AgentConfig, ExtensionLoadResult, GoosePlatform, MCP_PROTOCOL_VERSION}; pub use container::Container; pub use execute_commands::{context_management_unsupported_message, COMPACT_TRIGGERS}; pub use extension::{ExtensionConfig, ExtensionError}; diff --git a/crates/goose/src/agents/state_machine/tests/pipeline.rs b/crates/goose/src/agents/state_machine/tests/pipeline.rs index aff033505f..e92813301c 100644 --- a/crates/goose/src/agents/state_machine/tests/pipeline.rs +++ b/crates/goose/src/agents/state_machine/tests/pipeline.rs @@ -763,6 +763,8 @@ async fn build_test_pipeline( ExtensionManagerCapabilities { mcpui: false, host_info: None, + elicitation_handler: None, + protocol_version: None, }, false, )); diff --git a/crates/goose/src/oauth/mod.rs b/crates/goose/src/oauth/mod.rs index 3846fdde5d..09a52066e1 100644 --- a/crates/goose/src/oauth/mod.rs +++ b/crates/goose/src/oauth/mod.rs @@ -10,7 +10,7 @@ use minijinja::render; use oauth2::{Scope, TokenResponse}; use rmcp::transport::auth::{ AuthError, AuthorizationRequest, CredentialStore, OAuthClientConfig, OAuthState, - OAuthTokenResponse, StoredCredentials, + OAuthTokenResponse, StoredCredentials, WWWAuthenticateParams, }; use rmcp::transport::AuthorizationManager; use serde::Deserialize; @@ -26,9 +26,20 @@ const CLIENT_METADATA_URL: &str = "https://goose-docs.ai/oauth/client-metadata.j const DEFAULT_OAUTH_CALLBACK_TIMEOUT_SECS: u64 = 300; const OAUTH_CALLBACK_TIMEOUT_ENV: &str = "GOOSE_OAUTH_CALLBACK_TIMEOUT_SECONDS"; +/// Pre-registered OAuth client supplied by a probe script, for servers whose +/// authorization server supports neither Dynamic Client Registration nor +/// Client ID Metadata Documents. +#[derive(Clone, Debug, Default, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OAuthFlowConfig { + pub client_id: Option, + pub client_secret: Option, + pub client_metadata_url: Option, +} + #[derive(Clone)] struct AppState { - code_receiver: Arc>>>, + callback_receiver: Arc>>>, } #[derive(Debug, Deserialize)] @@ -62,14 +73,45 @@ fn announce_authorization_url(name: &str, authorization_url: &str) { ); } +async fn complete_automatic_authorization( + authorization_url: &str, + redirect_uri: &str, +) -> Result, anyhow::Error> { + if std::env::var_os("GOOSE_OAUTH_AUTOMATIC_CALLBACK").is_none() { + return Ok(None); + } + + let response = reqwest::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build()? + .get(authorization_url) + .send() + .await?; + let location = response + .headers() + .get(reqwest::header::LOCATION) + .ok_or_else(|| anyhow::anyhow!("authorization response did not include Location"))? + .to_str()?; + let callback_url = url::Url::parse(location)?; + let expected_redirect = url::Url::parse(redirect_uri)?; + if callback_url.scheme() != expected_redirect.scheme() + || callback_url.host_str() != expected_redirect.host_str() + || callback_url.port_or_known_default() != expected_redirect.port_or_known_default() + || callback_url.path() != expected_redirect.path() + { + anyhow::bail!("authorization response redirected to an unexpected callback URI"); + } + Ok(Some(callback_url.to_string())) +} + async fn wait_for_callback( - code_receiver: oneshot::Receiver, + callback_receiver: oneshot::Receiver, timeout_duration: Duration, name: &str, authorization_url: &str, -) -> Result { - match tokio::time::timeout(timeout_duration, code_receiver).await { - Ok(Ok(params)) => Ok(params), +) -> Result { + match tokio::time::timeout(timeout_duration, callback_receiver).await { + Ok(Ok(callback_url)) => Ok(callback_url), Ok(Err(e)) => Err(anyhow::anyhow!( "OAuth authorization for {} ended before the callback was received: {}", name, @@ -101,6 +143,22 @@ pub struct StaticOAuthClientConfig { pub scopes: Vec, } +/// Pre-registered client supplied through the environment, used by tools that +/// drive the flow without an extension config (`goose mcp-probe`, conformance +/// driver). +fn env_static_oauth_client() -> Option { + Some(StaticOAuthClientConfig { + client_id: std::env::var("GOOSE_MCP_OAUTH_CLIENT_ID").ok()?, + client_secret: std::env::var("GOOSE_MCP_OAUTH_CLIENT_SECRET").ok(), + scopes: Vec::new(), + }) +} + +fn client_metadata_url() -> String { + std::env::var("GOOSE_MCP_OAUTH_CLIENT_METADATA_URL") + .unwrap_or_else(|_| CLIENT_METADATA_URL.to_string()) +} + fn scope_set(scopes: &[String]) -> BTreeSet<&str> { scopes.iter().map(String::as_str).collect() } @@ -167,6 +225,9 @@ fn restore_omitted_scopes( fn build_authorization_request( redirect_uri: String, static_client: Option<&StaticOAuthClientConfig>, + challenge: Option, + mcp_server_url: &str, + previously_granted_scopes: &[String], ) -> AuthorizationRequest { let mut request = AuthorizationRequest::new(redirect_uri).with_client_name("goose"); match static_client { @@ -180,9 +241,34 @@ fn build_authorization_request( } } None => { - request = request.with_client_metadata_url(CLIENT_METADATA_URL); + request = request.with_client_metadata_url(client_metadata_url()); } } + + if let Some(challenge) = challenge { + // SEP-2350: a re-authorization triggered by a scope challenge requests + // the union of previously-granted scopes and the newly challenged + // scopes. The fresh AuthorizationManager has no scope memory, so seed + // the union from the stored grant. + let mut scopes = previously_granted_scopes.to_vec(); + scopes.extend( + static_client + .into_iter() + .flat_map(|client| client.scopes.iter().cloned()), + ); + if let Ok(base_url) = url::Url::parse(mcp_server_url) { + if let Some(challenged) = WWWAuthenticateParams::parse(&challenge, &base_url).scope { + scopes.extend(challenged.split_whitespace().map(str::to_string)); + } + } + let mut seen = BTreeSet::new(); + scopes.retain(|scope| seen.insert(scope.clone())); + if !scopes.is_empty() { + request = request.with_scopes(scopes); + } + request = request.with_challenge(challenge); + } + request } @@ -191,14 +277,33 @@ pub async fn oauth_flow( name: &String, static_client: Option<&StaticOAuthClientConfig>, ) -> Result { + oauth_flow_with_challenge(mcp_server_url, name, static_client, None).await +} + +pub async fn oauth_flow_with_challenge( + mcp_server_url: &String, + name: &String, + static_client: Option<&StaticOAuthClientConfig>, + challenge: Option, +) -> Result { + let env_client = env_static_oauth_client(); + let static_client = static_client.or(env_client.as_ref()); let credential_store = GooseCredentialStore::new(name.clone()); let mut auth_manager = AuthorizationManager::new(mcp_server_url).await?; auth_manager.set_credential_store(credential_store.clone()); let stored_credentials = credential_store.load().await?; let previous_requested_scopes = credential_store.load_requested_scopes()?; + let previously_granted_scopes = stored_credentials + .as_ref() + .map(|stored| stored.granted_scopes.clone()) + .unwrap_or_default(); - if auth_manager.initialize_from_store().await? { + // With a challenge in hand (e.g. a 403 insufficient_scope after a + // previously successful authorization), a refresh cannot satisfy the new + // scope requirement: skip straight to a full re-authorization that + // requests the union of scopes. + if auth_manager.initialize_from_store().await? && challenge.is_none() { let stored_credentials = stored_credentials .as_ref() .ok_or_else(|| anyhow::anyhow!("OAuth credentials disappeared during startup"))?; @@ -271,18 +376,26 @@ pub async fn oauth_flow( } } - // No existing credentials or they were invalid - need to do the full oauth flow - let (code_sender, code_receiver) = oneshot::channel::(); + let (callback_sender, callback_receiver) = oneshot::channel::(); let app_state = AppState { - code_receiver: Arc::new(Mutex::new(Some(code_sender))), + callback_receiver: Arc::new(Mutex::new(Some(callback_sender))), }; - let rendered = render!(CALLBACK_TEMPLATE, name => name); let handler = move |Query(params): Query, State(state): State| { let rendered = rendered.clone(); async move { - if let Some(sender) = state.code_receiver.lock().await.take() { - let _ = sender.send(params); + if let Some(sender) = state.callback_receiver.lock().await.take() { + let query = serde_urlencoded::to_string([ + ("code", params.code.as_str()), + ("state", params.state.as_str()), + ]) + .unwrap_or_default(); + let issuer = params + .iss + .as_deref() + .map(|iss| format!("&iss={}", urlencoding::encode(iss))) + .unwrap_or_default(); + let _ = sender.send(format!("http://callback/oauth_callback?{query}{issuer}")); } Html(rendered) } @@ -291,60 +404,66 @@ pub async fn oauth_flow( .route("/oauth_callback", get(handler)) .with_state(app_state); - let port: u16 = std::env::var("GOOSE_OAUTH_CALLBACK_PORT") + let port = std::env::var("GOOSE_OAUTH_CALLBACK_PORT") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(0); - let addr = SocketAddr::from(([127, 0, 0, 1], port)); - let listener = tokio::net::TcpListener::bind(addr).await?; + let listener = tokio::net::TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], port))).await?; let used_addr = listener.local_addr()?; let server_handle = tokio::spawn(async move { - let result = axum::serve(listener, app).await; - if let Err(e) = result { + if let Err(e) = axum::serve(listener, app).await { eprintln!("Callback server error: {}", e); } }); let mut oauth_state = OAuthState::new(mcp_server_url, None).await?; - let redirect_uri = format!("http://127.0.0.1:{}/oauth_callback", used_addr.port()); oauth_state - .start_authorization(build_authorization_request(redirect_uri, static_client)) + .start_authorization(build_authorization_request( + redirect_uri.clone(), + static_client, + challenge, + mcp_server_url, + &previously_granted_scopes, + )) .await?; let authorization_url = oauth_state.get_authorization_url().await?; - announce_authorization_url(name, authorization_url.as_str()); - if let Err(e) = webbrowser::open(authorization_url.as_str()) { - warn!( - "[OAuth:{}] Failed to open browser automatically: {}", - name, e - ); + let callback_url = async { + if let Some(callback_url) = + complete_automatic_authorization(authorization_url.as_str(), &redirect_uri).await? + { + Ok(callback_url) + } else { + announce_authorization_url(name, authorization_url.as_str()); + if let Err(e) = webbrowser::open(authorization_url.as_str()) { + warn!( + "[OAuth:{}] Failed to open browser automatically: {}", + name, e + ); + } + wait_for_callback( + callback_receiver, + oauth_callback_timeout(), + name, + authorization_url.as_str(), + ) + .await + } } - - let callback_params = wait_for_callback( - code_receiver, - oauth_callback_timeout(), - name, - authorization_url.as_str(), - ) .await; server_handle.abort(); - let CallbackParams { - code: auth_code, - state: csrf_token, - iss, - } = callback_params?; - oauth_state - .handle_callback_with_issuer(&auth_code, &csrf_token, iss.as_deref()) - .await?; + oauth_state.handle_callback_url(&callback_url?).await?; let (client_id, token_response) = oauth_state.get_credentials().await?; - let mut auth_manager = oauth_state .into_authorization_manager() .ok_or_else(|| anyhow::anyhow!("Failed to get authorization manager"))?; - let granted_scopes = auth_manager.get_current_scopes().await; + let granted_scopes = match token_response.as_ref().and_then(|tr| tr.scopes()) { + Some(scopes) => scopes.iter().map(|scope| scope.to_string()).collect(), + None => auth_manager.get_current_scopes().await, + }; credential_store.save_with_requested_scopes( StoredCredentials::new( client_id, @@ -394,17 +513,12 @@ mod tests { } #[tokio::test] - async fn wait_for_callback_returns_received_callback_params() { + async fn wait_for_callback_returns_received_callback_url() { let (sender, receiver) = oneshot::channel(); - sender - .send(CallbackParams { - code: "auth-code".to_string(), - state: "csrf-state".to_string(), - iss: Some("https://auth.example".to_string()), - }) - .unwrap(); + let expected = "http://callback/oauth_callback?code=auth-code&state=csrf-state"; + sender.send(expected.to_string()).unwrap(); - let params = wait_for_callback( + let callback_url = wait_for_callback( receiver, Duration::from_secs(1), "test-server", @@ -413,9 +527,7 @@ mod tests { .await .unwrap(); - assert_eq!(params.code, "auth-code"); - assert_eq!(params.state, "csrf-state"); - assert_eq!(params.iss.as_deref(), Some("https://auth.example")); + assert_eq!(callback_url, expected); } #[test] @@ -557,8 +669,13 @@ mod tests { #[test] fn authorization_request_uses_client_metadata_url_without_static_client() { - let request = - build_authorization_request("http://127.0.0.1:1234/oauth_callback".to_string(), None); + let request = build_authorization_request( + "http://127.0.0.1:1234/oauth_callback".to_string(), + None, + None, + "https://mcp.example", + &[], + ); assert_eq!(request.client_id, None); assert_eq!(request.client_secret, None); @@ -580,6 +697,9 @@ mod tests { let request = build_authorization_request( "http://127.0.0.1:1234/oauth_callback".to_string(), Some(&static_client), + None, + "https://mcp.example", + &[], ); assert_eq!(request.client_id.as_deref(), Some("registered-client")); @@ -599,6 +719,9 @@ mod tests { let request = build_authorization_request( "http://127.0.0.1:1234/oauth_callback".to_string(), Some(&static_client), + None, + "https://mcp.example", + &[], ); assert_eq!(request.client_id.as_deref(), Some("registered-client")); @@ -607,6 +730,45 @@ mod tests { assert!(request.scopes.is_empty()); } + #[test] + fn challenge_request_asks_for_the_union_of_granted_and_challenged_scopes() { + let request = build_authorization_request( + "http://127.0.0.1:1234/oauth_callback".to_string(), + None, + Some( + r#"Bearer error="insufficient_scope", scope="scope.write scope.admin""#.to_string(), + ), + "https://mcp.example", + &["scope.read".to_string(), "scope.write".to_string()], + ); + + assert_eq!( + request.scopes, + vec!["scope.read", "scope.write", "scope.admin"] + ); + assert!(request.challenge.is_some()); + } + + #[test] + fn challenge_request_keeps_static_client_scopes() { + let static_client = StaticOAuthClientConfig { + client_id: "registered-client".to_string(), + client_secret: None, + scopes: vec!["scope.read".to_string()], + }; + + let request = build_authorization_request( + "http://127.0.0.1:1234/oauth_callback".to_string(), + Some(&static_client), + Some(r#"Bearer error="insufficient_scope", scope="scope.write""#.to_string()), + "https://mcp.example", + &[], + ); + + assert_eq!(request.client_id.as_deref(), Some("registered-client")); + assert_eq!(request.scopes, vec!["scope.read", "scope.write"]); + } + #[tokio::test] async fn wait_for_callback_times_out_with_authorization_url() { let (_sender, receiver) = oneshot::channel(); diff --git a/crates/goose/tests/mcp_integration_test.rs b/crates/goose/tests/mcp_integration_test.rs index 5a23a17a07..1808a5dfac 100644 --- a/crates/goose/tests/mcp_integration_test.rs +++ b/crates/goose/tests/mcp_integration_test.rs @@ -12,7 +12,7 @@ use tokio_util::sync::CancellationToken; use goose::agents::extension::{Envs, ExtensionConfig}; use goose::agents::extension_manager::{ExtensionManager, ExtensionManagerCapabilities}; -use goose::agents::GoosePlatform; +use goose::agents::{GoosePlatform, MCP_PROTOCOL_VERSION}; use goose_providers::model::ModelConfig; use test_case::test_case; @@ -167,18 +167,23 @@ async fn test_replayed_session( tool_calls: Vec, required_envs: Vec<&str>, ) { + // The working directory is sent to the server verbatim in our `roots/list` + // response, so it is part of the recorded protocol traffic. It must be an + // absolute path (relative paths are not convertible to a `file://` URL) and + // it must be stable across machines, otherwise playback compares a recorded + // path against whatever cwd the test happens to run in. + const TEST_WORKING_DIR: &str = "/tmp/goose_test"; + fs::create_dir_all(TEST_WORKING_DIR).ok(); + let _env = env_lock::lock_env([ ("GOOSE_MCP_CLIENT_VERSION", Some("0.0.0")), ("GOOSE_PROVIDER", Some("openai")), ("GOOSE_MODEL", Some("gpt-4o")), - ("GOOSE_WORKING_DIR", Some("/tmp/goose_test")), + ("GOOSE_WORKING_DIR", Some(TEST_WORKING_DIR)), ]); // Setup test file for developer extension tests let test_file_path = "/tmp/goose_test/goose.txt"; - if let Some(parent) = std::path::Path::new(test_file_path).parent() { - fs::create_dir_all(parent).ok(); - } fs::write(test_file_path, "# goose\n").ok(); let replay_file_name = command .iter() @@ -257,6 +262,8 @@ async fn test_replayed_session( ExtensionManagerCapabilities { mcpui: true, host_info: None, + elicitation_handler: None, + protocol_version: Some(MCP_PROTOCOL_VERSION), }, true, )); diff --git a/crates/goose/tests/mcp_replays/github-mcp-serverstdio b/crates/goose/tests/mcp_replays/github-mcp-serverstdio index 789fb35d08..4a67e4073a 100644 --- a/crates/goose/tests/mcp_replays/github-mcp-serverstdio +++ b/crates/goose/tests/mcp_replays/github-mcp-serverstdio @@ -1,14 +1,14 @@ STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}},"roots":{},"sampling":{},"elicitation":{}},"clientInfo":{"name":"goose-desktop","version":"0.0.0"}}} -STDERR: time=2026-07-29T10:52:54.153-04:00 level=INFO msg="starting server" version=1.7.0 host="" readOnly=false lockdownEnabled=false +STDERR: time=2026-08-11T11:30:18.507-04:00 level=INFO msg="starting server" version=1.7.0 host="" readOnly=false lockdownEnabled=false STDERR: GitHub MCP Server running on stdio -STDERR: time=2026-07-29T10:52:54.158-04:00 level=INFO msg="server run start" -STDERR: time=2026-07-29T10:52:54.158-04:00 level=INFO msg="server connecting" -STDERR: time=2026-07-29T10:52:54.158-04:00 level=INFO msg="server session connected" session_id="" +STDERR: time=2026-08-11T11:30:18.514-04:00 level=INFO msg="server run start" +STDERR: time=2026-08-11T11:30:18.514-04:00 level=INFO msg="server connecting" +STDERR: time=2026-08-11T11:30:18.515-04:00 level=INFO msg="server session connected" session_id="" STDOUT: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"completions":{},"logging":{},"prompts":{"listChanged":true},"resources":{"listChanged":true},"tools":{"listChanged":true}},"instructions":"The GitHub MCP Server provides tools to interact with GitHub platform.\n\nTool selection guidance:\n\t1. 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.\n\t2. 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).\n\nContext management:\n\t1. Use pagination whenever possible with batches of 5-10 items.\n\t2. Use minimal_output parameter set to true if the full information is not needed to accomplish a task.\n\nTool usage guidance:\n\t1. 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. Always call 'get_me' first to understand current user permissions and context. ## Issues\n\nCheck '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. ## Pull Requests\n\nPR 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.\n\nBefore 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.","protocolVersion":"2025-11-25","serverInfo":{"name":"github-mcp-server","title":"GitHub MCP Server","version":"1.7.0","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAADK0lEQVRIibWVQWhcVRSGv3Pfy0zGdBIMRWk7iziMTpORptpqiNSNG1dNizaFFlxIoYuCC7NwJdiVlhYprtwpighCF9GtuhCRtuCoaWdqGF7HkE4aWm1CpklmJpl3j4vMS99M2k4G9N+8d8659/8u9553H/zPkscVM5lMf03do6iOKbJXIAGgUBJ0GpHvolKfzOfzCx0BEonRWDRemRD0PaC3zSKXED1fLfdcLJUuV9oC0ukXdluxkyq81Ma4VX9Y4x4p5rOzjwSkUvsSdMkVYE+H5oHmjM9IoTA1FyRM8JJIjMbokm83zUW/Vvi5raXwC8rnjWiPdXRyYGCgOyi7wUs0XpkAXmyEa/XV8qmZmZnqc0P7XrXKW2BuKDovgorqLoWMFb4p3rj2I4w7qcHCMSAOctDt7n0X+GiDT6NbrPsXDw50xftzKg7odvcmtXf4DsJTjXBpzbXPzF6/vmgAauoepblbnGTyQLvu2VQmk4kgxEKpvui6cwSCM1Ada5qheq5YzC5tF5DP59dQzjYlZcOzccgyGK65mC+2ax7IWPfLcGxhKARgV7i4vBy70ymgUMj+A9SDWGB3GOCEB/f0VJ/sFJBKjfQS6srAOwD8HR7su/7BTgE2Umn98u9uAgR+ayqpnOkUIK1zZMPTACjyQzOA11ND+z+E8aate4TMs4PD7wu80ezP940nJJMH+ky0XgKeAHkH1TcRXgNywFeoueJN//5T2CCZGT7kWBlV9CSwvwV6n/XuhOddLRuAYjG7hMjHgEH1tEb0BEgWeB44J8aebF2243Nc0fMPMUfgguddLUOoe/r7dlwWJzqGMCxq6q5Wz/jSVQZ+Nb795N69u4thk/6dT7uInNiyYcoU9ZW3FxYWfAjdpp7n1XzjHAbmUJ3wTXQ8gvOZb+oXV1d3zLf6WEcWW3MoJXX9w57n1YKUCdeL+eysOv4oyrQqn65Tv+1adz4WrxzaskXWNs0V5Jq6/is3c7lb4byhRTdzuVvUV0ZQPgDuA6jqlj+fqhvkllE9q+vLL7eab4Afo1RqpJdI9Rhr3ZeCQwuUTqfjvoket7WuS51cjP+5/gWC8y5uIkrtDQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAB8ElEQVRIibWVu09UQRSHv9k1IgW7AY0RBWKsTGx9ND4qS5F/wEYLDb0xAY0UxkdrZ2NHYWfsaYyVT4iJwZpNjJooLBQEYz6LvZsdxmH3LtFfN3PO+X5zzzwu/GeFbkF1BJgCJoHjwFgRagDLwAvgeQjhR1+u6qA6q67ZW6vqjDpYFn5YfV0CnOqDOtELPqY2dgFvq6Ee6daWd1HyvPqyBPSV+jQav1H3tbkhMpgF7hXDLaAeQthUzwFXgE/AF0BgFDgBPAshLKhV4CcwVNTPhBAexKsfcfuGbqhdT1imA1+j+lV1GKBSxKeAWpRfTca94HuB+BTVgcuxwWRS8zCEsFbWIISwBcwl0x2m+jnZuKNl4RHjQMJYjoPNJFju0vxt8itiNKHTomqSO7wLeA3YE01VYoPvSf7Jfg2AU8n4W2zwPglO78Igrekw1enMDb1fXKCuUivq7Uz99Tiprq6rvwuzhSLpo3pLvZABn1Vv2nrkUjWLPdlWMFcEF9WD6tuo4EnG4HEG3Nad3KcOqEtRe/bb+ic8Uo9l8i/tAF9UB3bq54StJ3dTvaGOqofM3IuiRalW1PH8bnUKx4tVxLqYyTuf5Czl4JV0IoSwApwB7gLr7enMWtpzG7TeodNFbXmpNfWq6YloxYbUa2q9L+i/1h8/EAGdUrF9ZQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]}}} STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"} -STDERR: time=2026-07-29T10:52:54.159-04:00 level=INFO msg="session initialized" +STDERR: time=2026-08-11T11:30:18.516-04:00 level=INFO msg="session initialized" STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"agent-session-id":"test-session-id","progressToken":0}}} STDOUT: {"jsonrpc":"2.0","id":1,"result":{"ttlMs":0,"cacheScope":"public","tools":[{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Add review comment to the requester's latest pending pull request review"},"description":"Add review comment to the requester's latest pending pull request review. A pending review needs to already exist to call this (check with the user if not sure).","inputSchema":{"type":"object","properties":{"body":{"type":"string","description":"The text of the review comment"},"line":{"type":"number","description":"The line of the blob in the pull request diff that the comment applies to. For multi-line comments, the last line of the range"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"path":{"type":"string","description":"The relative path to the file that necessitates a comment"},"pullNumber":{"type":"number","description":"Pull request number"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"side":{"type":"string","description":"The side of the diff to comment on. LEFT indicates the previous state, RIGHT indicates the new state","enum":["LEFT","RIGHT"]},"startLine":{"type":"number","description":"For multi-line comments, the first line of the range that the comment applies to"},"startSide":{"type":"string","description":"For multi-line comments, the starting side of the diff that the comment applies to. LEFT indicates the previous state, RIGHT indicates the new state","enum":["LEFT","RIGHT"]},"subjectType":{"type":"string","description":"The level at which the comment is targeted","enum":["FILE","LINE"]}},"required":["owner","repo","pullNumber","path","body","subjectType"]},"name":"add_comment_to_pending_review","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Add comment to issue or pull request"},"description":"Add a comment and/or reaction to a specific issue or issue comment in a GitHub repository. Use this tool with pull requests as well (in this case pass pull request number as issue_number), but only if user is not asking specifically to add or react to review comments. At least one of body or reaction is required.","inputSchema":{"type":"object","properties":{"body":{"type":"string","description":"Comment content. Required unless reaction is provided."},"comment_id":{"type":"number","description":"The numeric ID of the issue or pull request comment to react to. Use this for reactions to comments; omit it to react to the issue or pull request itself. Cannot be combined with body.","minimum":1},"issue_number":{"type":"number","description":"Issue or pull request number to comment on or react to."},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"reaction":{"type":"string","description":"Emoji reaction to add. Required unless body is provided.","enum":["+1","-1","laugh","confused","heart","hooray","rocket","eyes"]},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","issue_number"]},"name":"add_issue_comment","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Add reply to pull request comment"},"description":"Add a reply and/or reaction to an existing pull request comment. This can create a new comment linked as a reply to the specified comment, add an emoji reaction to the specified comment, or do both. At least one of body or reaction is required.","inputSchema":{"type":"object","properties":{"body":{"type":"string","description":"The text of the reply. Required unless reaction is provided."},"commentId":{"type":"number","description":"The numeric ID of the pull request review comment to reply or react to. Use the number from a #discussion_r... anchor, not the GraphQL thread node ID (PRRT_...).","minimum":1},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"pullNumber":{"type":"number","description":"Pull request number. Required when body is provided."},"reaction":{"type":"string","description":"Emoji reaction to add. Required unless body is provided.","enum":["+1","-1","laugh","confused","heart","hooray","rocket","eyes"]},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","commentId"]},"name":"add_reply_to_pull_request_comment","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":true,"readOnlyHint":false,"title":"Assign Copilot to issue"},"description":"Assign Copilot to a specific issue in a GitHub repository.\n\nThis tool can help with the following outcomes:\n- a Pull Request created with source code changes to resolve the issue\n\n\nMore information can be found at:\n- https://docs.github.com/en/copilot/concepts/agents/cloud-agent/about-cloud-agent\n","inputSchema":{"type":"object","properties":{"base_ref":{"type":"string","description":"Git reference (e.g., branch) that the agent will start its work from. If not specified, defaults to the repository's default branch"},"custom_instructions":{"type":"string","description":"Optional custom instructions to guide the agent beyond the issue body. Use this to provide additional context, constraints, or guidance that is not captured in the issue description"},"issue_number":{"type":"number","description":"Issue number"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","issue_number"]},"name":"assign_copilot_to_issue","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC20lEQVRIidWUS4wMURSGv3O7kWmPEMRrSMzcbl1dpqtmGuOxsCKECCKxEBusSJhIWEhsWLFAbC1sWFiISBARCyQ2kzSZGaMxHokgXvGIiMH0PRZjpJqqHpb+TeX+59z//H/q5sD/DqlX9H1/zFeX2qzIKoFWYDKgwBtUymL0UkNaT3V3d3/+5wG2EGxB9TDIxGFMvhVhb9/drpN/NaDJC7MGdwJk6TDCv0Gvq0lve9R762GUNdFDLleaZNBrICGq+4yhvf9TJtP/KZNB2PrLlbBliBfRhajuAwnFVa/n8/nkxFkv3GO9oJrzgwVxdesV71ov6I2r5fxggfWCatYL9yYmUJgLPH7Q29WZ4OED6Me4wuAdeQK6MMqna9t0GuibBHFAmgZ9JMG9BhkXZWoSCDSATIq7aguBD0wBplq/tZBgYDIwKnZAs99mFRYD9vd/YK0dpcqhobM6d9haWyOULRTbAauwuNlvsxHTYP3iBnVyXGAa8BIYC3oVeAKioCtAPEE7FCOgR0ErIJdBBZgNskzh40+NF6K6s+9e91lp9osrxMnFoTSmSmPVsF+E5cB0YEDgtoMjjypd5wCy+WC9GnajhEAa4bkqV9LOHKwa9/yneYeyUqwX3AdyQ5EeVrrqro/hYL0g+ggemKh4HGbPmVu0+fB8U76lpR6XgJwZpoGUpNYiusZg1tXjkmCAav0OMTXfJC4eVYPqwbot6l4BCPqyLhd7lwMAWC/cYb3gi/UCzRaKOxsbFzVEM1iv2Ebt5v2Dm14qZbJecZf1Ah3UCrcTbbB+awHnjgHLgHeinHYqZ8aPSXWWy+XvcQZLpdKI9/0D7UbZiLIJmABckVSqo+/OrUrNgF+D8q1LEdcBrAJGAJ8ROlGeicorABWdAswE5gOjge8CF8Ad66v03IjqJb75WS0tE0YOmNWqLBGReaAzgIkMLrt3oM9UpSzCzW9pd+FpT8/7JK3/Gz8Ao5X6wtwP7N4AAAAASUVORK5CYII=","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACCElEQVRIid2UPWsUYRSFn3dxWWJUkESiBgslFokfhehGiGClBBQx4h9IGlEh2ijYxh+gxEL/hIWwhYpF8KNZsFRJYdJEiUbjCkqisj4W+y6Mk5nd1U4PDMOce+45L3fmDvzXUDeo59WK+kb9rn5TF9R76jm1+2/NJ9QPtseSOv4nxrvVmQ6M05hRB9qZ98ZR1NRralntitdEwmw8wQ9HbS329rQKuKLW1XJO/aX6IqdWjr1Xk/y6lG4vMBdCqOacoZZ3uBBCVZ0HDrcK2AYs5ZkAuwBb1N8Dm5JEISXoAnqzOtU9QB+wVR3KCdgClDIr6kCc4c/0O1BLNnahiYpaSmmGY62e/JpCLJ4FpmmMaBHYCDwC5mmMZBQYBC7HnhvAK+B+fN4JHAM+R4+3wGQI4S7qaExtol+9o86pq+oX9Yk6ljjtGfVprK2qr9Xb6vaET109jjqb3Jac2XaM1PLNpok1Aep+G/+dfa24nADTX1EWTgOngLE2XCYKQL0DTfKex2WhXgCutxG9i/fFNlwWpgBQL6orcWyTaldToRbUA2pow61XL0WPFfXCb1HqkPowCj6q0+qIWsw7nlpUj6i31OXY+0AdbGpCRtNRGgt1AigCX4EqsJAYTR+wAzgEdAM/gApwM4TwOOm3JiARtBk4CYwAB4F+oIfGZi/HwOfAM6ASQviU5/Vv4xcBzmW2eT1nrQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Create branch"},"description":"Create a new branch in a GitHub repository","inputSchema":{"type":"object","properties":{"branch":{"type":"string","description":"Name for new branch"},"from_branch":{"type":"string","description":"Source branch (defaults to repo default)"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","branch"]},"name":"create_branch","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Create or update file"},"description":"Create or update a single file in a GitHub repository. \nIf updating, you should provide the SHA of the file you want to update. Use this tool to create or update a file in a GitHub repository remotely; do not use it for local file operations.\n\nIn order to obtain the SHA of original file version before updating, use the following git command:\ngit rev-parse :\n\nSHA MUST be provided for existing file updates.\n","inputSchema":{"type":"object","properties":{"branch":{"type":"string","description":"Branch to create/update the file in"},"content":{"type":"string","description":"Content of the file"},"message":{"type":"string","description":"Commit message"},"owner":{"type":"string","description":"Repository owner (username or organization)","x-mcp-header":"owner"},"path":{"type":"string","description":"Path where to create/update the file"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"sha":{"type":"string","description":"The blob SHA of the file being replaced. Required if the file already exists."}},"required":["owner","repo","path","content","message","branch"]},"name":"create_or_update_file","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Open new pull request"},"description":"Create a new pull request in a GitHub repository.","inputSchema":{"type":"object","properties":{"base":{"type":"string","description":"Branch to merge into"},"body":{"type":"string","description":"PR description"},"draft":{"type":"boolean","description":"Create as draft PR"},"head":{"type":"string","description":"Branch containing changes"},"maintainer_can_modify":{"type":"boolean","description":"Allow maintainer edits"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"reviewers":{"type":"array","items":{"type":"string"},"description":"GitHub usernames or ORG/team-slug team reviewers to request reviews from"},"title":{"type":"string","description":"PR title"}},"required":["owner","repo","title","head","base"]},"name":"create_pull_request","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Create repository"},"description":"Create a new GitHub repository in your account or specified organization","inputSchema":{"type":"object","properties":{"autoInit":{"type":"boolean","description":"Initialize with README"},"description":{"type":"string","description":"Repository description"},"name":{"type":"string","description":"Repository name"},"organization":{"type":"string","description":"Organization to create the repository in (omit to create in your personal account)"},"private":{"type":"boolean","description":"Whether the repository should be private. Defaults to true (private) when omitted.","default":true}},"required":["name"]},"name":"create_repository","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"destructiveHint":true,"idempotentHint":false,"readOnlyHint":false,"title":"Delete file"},"description":"Delete a file from a GitHub repository","inputSchema":{"type":"object","properties":{"branch":{"type":"string","description":"Branch to delete the file from"},"message":{"type":"string","description":"Commit message"},"owner":{"type":"string","description":"Repository owner (username or organization)","x-mcp-header":"owner"},"path":{"type":"string","description":"Path to the file to delete"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","path","message","branch"]},"name":"delete_file","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Fork repository"},"description":"Fork a GitHub repository to your account or specified organization","inputSchema":{"type":"object","properties":{"organization":{"type":"string","description":"Organization to fork to"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo"]},"name":"fork_repository","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACuElEQVRIibWTTUhUYRiFn/fOdYyoydQxk4LEGzN3RudaLYL+qRaBQYsIItoHCW37ISNbRwUFLWoRZBEt+4EIooKoTdZQ6TWaNIgouzJkuGhG731b6JTojDNBntX3ne+c97zfH8wzZCbREm9bZ4hsQvkeDvl3+/r6xuYqEIvFFgdSvRuDqCrPMu6bVyUDrITTjdI1jR8KBbrj/fs3Q8WLp5p9Qx4BzVOUInIm058+XdAY0ztH6RLhSpAza1RlI2jENzhfqntfjAugEdTYMFEtS0GvonrKslNrZwWIhDYDMh6Wo4ODvaMfB9LPFaMHZGvJ8xHdAlzPDLx+8Smd/pE39SggAptnB2gwDBD6ReJvhSCpMFyq/uSa/NFX5UMJgGCaxywMwiH/bi4wh0SCOy1x5waiCUF2gnSW3AByEfSSZTsPVXFF9CDC4ALx7xU0ocLA87x8tG7ZHRUShsheVMKInMy46culArIj317WRpd7KB2GsAl4bKoccN2330t5ALBsJ7ASTvecoun6hNNt2U5QbM0oRip8E6Wt0gCUFPC12FKoGFnX0BgBDtVGG3/W1qzqz2a/5IrpLGt9pLahvhPhCKrnsiPDT2dqZv1kgGQyGc4FZg+wr8I93F6y0DzY29s7XlHAnw7j7dswgg2oRCYZPTBluzk51VEwXmQG0k8qbGRuWHbqiWWn/qlY0Uv+n5j3gKKvaCaSyeSimrqms4hsB4kurW9c0bSs/pnneflyXrOcACCn5jWEPSr0AAgczvlVTVT+ykojFlvTZNmOWvHU8QJnJVInLNtR2163vJy/7B0EpjYAqBhugVMVF8A3goZy/rJHFGa8P4fpCXosHm9PqwbiwzHAqyLvlvPP+dEKWG23dyh6C1g0RY0Jsv+Dm77/XwIAWlpbVzJh7gLAnHjw8d27z5V65xW/AVGM6Ekx9nZCAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABoUlEQVRIibWUPS+EQRSFz0hsxaIgIZHYllDYiiiUEn6Bn0Dho6Nhf4GPjkYn8ZEgGqFRSNBoVTQKdhEROsk+mrt2svvu7Gutk7yZzJlz77nzztyR/hmulADSkkYk5SQdO+c+QwmAZkkTktolXTjnbkLiDJCniHsgFdCnTFNAHliuJE6bYANoAYaBF+AwYHBkmiGgFdi0HINR4lmrotXjVoG3gMEbsOLN2yzHTIFr8PRZG3s9rs/jo5At0fd6fFk1TfY/X4A14MyqmQrsYNo0pxbzCtwBTZUCUsAh8GHCKaDspnl6ZyZ3FnMA9AR2/BOYBzJVhUV9BshHrTVEkZKeJPXHNZA0IOkxttrrhzkgGdAlgXnTLv3GIAHsEh87QGNUrooHaEajkoYlFXYxaeO2je+SLp1z57Grr2J4DvwqWaVDrhv+3SAWrMvXgWcgZ10b3a01GuwDX8CWfV/AXr2Sd9lVXPC4ReM6q8XHOYMOG2897rZkrXZY0+WAK6DHHsRr4xJ/NjCTcXstC/gAxuPEBju5xKRb0phNT5xzD7UUW3d8A4p92DZKdSwEAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get commit details"},"description":"Get details for a commit from a GitHub repository","inputSchema":{"type":"object","properties":{"detail":{"type":"string","description":"Level of detail to include for changed files. \"none\" omits stats and files entirely. \"stats\" (default) includes per-file metadata: filename, status, and lines-of-code counts (additions, deletions, changes), with no patch content. \"full_patch\" additionally includes the unified diff content for each file and can be very large.","default":"stats","enum":["none","stats","full_patch"]},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"sha":{"type":"string","description":"Commit SHA, branch name, or tag name"}},"required":["owner","repo","sha"]},"name":"get_commit","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get file or directory contents"},"description":"Get the contents of a file or directory from a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner (username or organization)","x-mcp-header":"owner"},"path":{"type":"string","description":"Path to file/directory","default":"/"},"ref":{"type":"string","description":"Accepts optional git refs such as `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head`"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"sha":{"type":"string","description":"Accepts optional commit SHA. If specified, it will be used instead of ref"}},"required":["owner","repo"]},"name":"get_file_contents","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get a specific label from a repository"},"description":"Get a specific label from a repository.","inputSchema":{"type":"object","properties":{"name":{"type":"string","description":"Label name."},"owner":{"type":"string","description":"Repository owner (username or organization name)","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","name"]},"name":"get_label","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get latest release"},"description":"Get the latest release in a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo"]},"name":"get_latest_release","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get my user profile"},"description":"Get details of the authenticated GitHub user. Use this when a request is about the user's own profile for GitHub. Or when information is missing to build other tool calls.","inputSchema":{"type":"object","properties":{}},"name":"get_me","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACSUlEQVRIidWVv09TURTHP+e+2mJMEOLiQBig4GtN4WEHRQxGMfHHYkx0YpJJR+OmjMTNP0CjRv4DJyNETXTQODRQSkmJTweYBUw0KeX1HoeCKY3v0eKi3/Gdc74/zrs3F/53SJN9Jul6owhpAJQlvzT/HrB/LZB0h4YR+wjI7B6UBVW55ZfmPkY6a4J8FugQ1euOLbc7ttyuyDWLJhA7k0wNnNpvAulLefOKdiZM4BWLxbX6Yncm0xkPTB5lzS/lhwBtKUHS9c4qOiCqdxrJAVYKhXVE7iIMJt2h0TCe8BVt/1Cjm7OhPZXETK23mm5ZQMTGQom3YW0gACoS2hsqoNbJAwTm4FjocFtwEcCozYcajTBo+lLenEUTWzE7vFIorNcXXdc9EpCYB775pfwJQu5E1BqsVW6L8CoemHwy7d39vfN4+VJgeYhwGPRGGPleCQCkN+XdE3Tqz1W97y8tPIgkCCv092dc68gzkGFgHXiNsFrLJt2IjgGdwAfH6sTy8sJy0wK9xwbPieEFSlmRyY5DzvNcLrdV35PNZg9s/KzeFNEpII6aq35p7t2eAjXn5hOwqk718pfFxdXQ/EDP8Wy3scFLoMuxerIxSeMxFeuYpyjlZsgBvhZzK9bErgAVa+RJo+ldAn0p7wJwWpHJZsjrRVRlUuFMT3rgfEQCOw5stDlb082S70CCH9PAd2NlPFRAkRGEN8VisdKqgO/7mwJvEUZCBYCjwOdWyXdga7Nd9d9232SRCSo28oWKgjjVxxrElvY7/2/iF/Bu47CZ2fOnAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABaElEQVRIidWUvUrDcBTFz79YnRSii4N2cVB8AuvgByKuQqmrILQPoe9SfABxV5wUna2IVLq1k11adffn4A3UNPknjQ56IARyzz3ncj8i/Xe4LCSgIGlD0qp9epJ07Zz7+HEFQBl4YBRNoPwb4u9AB6gA0/bsAy3gDVjLK+6syg4wGxMPLHYPZGp1VGDLWlHxcKrG2UziFDwe4UAvPZyLCHcsgwlPLETYmkSuz6Bp7x0PZy/CzQ6gYENuAUFMfA7o2pB9hXpN1m0VOzbQGXsOTDz/mpqBA05ijizEcZpG4v4CK5IaksqS+pKuJHUtXNLXbAJJd5KOnHPP41S+DbwCL0ANKMZwikAd6AED3y2MVG7ij8BiBn7JuANgOY3sgFurPFU8YtIDbry/DWDXhlfLKj6UW7fc5LsBToE+MJnDYMra1PCR2sDZuOJD+efAt22KXuC8pHZeA8td8FVQBZIJKQCWgMO8+X8Tn12zhtgfmPjeAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get a release by tag name"},"description":"Get a specific release by its tag name in a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"tag":{"type":"string","description":"Tag name (e.g., 'v1.0.0')"}},"required":["owner","repo","tag"]},"name":"get_release_by_tag","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get tag details"},"description":"Get details about a specific git tag in a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"tag":{"type":"string","description":"Tag name"}},"required":["owner","repo","tag"]},"name":"get_tag","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get team members"},"description":"Get member usernames of a specific team in an organization. Limited to organizations accessible with current credentials","inputSchema":{"type":"object","properties":{"org":{"type":"string","description":"Organization login (owner) that contains the team."},"team_slug":{"type":"string","description":"Team slug"}},"required":["org","team_slug"]},"name":"get_team_members","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACSUlEQVRIidWVv09TURTHP+e+2mJMEOLiQBig4GtN4WEHRQxGMfHHYkx0YpJJR+OmjMTNP0CjRv4DJyNETXTQODRQSkmJTweYBUw0KeX1HoeCKY3v0eKi3/Gdc74/zrs3F/53SJN9Jul6owhpAJQlvzT/HrB/LZB0h4YR+wjI7B6UBVW55ZfmPkY6a4J8FugQ1euOLbc7ttyuyDWLJhA7k0wNnNpvAulLefOKdiZM4BWLxbX6Yncm0xkPTB5lzS/lhwBtKUHS9c4qOiCqdxrJAVYKhXVE7iIMJt2h0TCe8BVt/1Cjm7OhPZXETK23mm5ZQMTGQom3YW0gACoS2hsqoNbJAwTm4FjocFtwEcCozYcajTBo+lLenEUTWzE7vFIorNcXXdc9EpCYB775pfwJQu5E1BqsVW6L8CoemHwy7d39vfN4+VJgeYhwGPRGGPleCQCkN+XdE3Tqz1W97y8tPIgkCCv092dc68gzkGFgHXiNsFrLJt2IjgGdwAfH6sTy8sJy0wK9xwbPieEFSlmRyY5DzvNcLrdV35PNZg9s/KzeFNEpII6aq35p7t2eAjXn5hOwqk718pfFxdXQ/EDP8Wy3scFLoMuxerIxSeMxFeuYpyjlZsgBvhZzK9bErgAVa+RJo+ldAn0p7wJwWpHJZsjrRVRlUuFMT3rgfEQCOw5stDlb082S70CCH9PAd2NlPFRAkRGEN8VisdKqgO/7mwJvEUZCBYCjwOdWyXdga7Nd9d9232SRCSo28oWKgjjVxxrElvY7/2/iF/Bu47CZ2fOnAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABaElEQVRIidWUvUrDcBTFz79YnRSii4N2cVB8AuvgByKuQqmrILQPoe9SfABxV5wUna2IVLq1k11adffn4A3UNPknjQ56IARyzz3ncj8i/Xe4LCSgIGlD0qp9epJ07Zz7+HEFQBl4YBRNoPwb4u9AB6gA0/bsAy3gDVjLK+6syg4wGxMPLHYPZGp1VGDLWlHxcKrG2UziFDwe4UAvPZyLCHcsgwlPLETYmkSuz6Bp7x0PZy/CzQ6gYENuAUFMfA7o2pB9hXpN1m0VOzbQGXsOTDz/mpqBA05ijizEcZpG4v4CK5IaksqS+pKuJHUtXNLXbAJJd5KOnHPP41S+DbwCL0ANKMZwikAd6AED3y2MVG7ij8BiBn7JuANgOY3sgFurPFU8YtIDbry/DWDXhlfLKj6UW7fc5LsBToE+MJnDYMra1PCR2sDZuOJD+efAt22KXuC8pHZeA8td8FVQBZIJKQCWgMO8+X8Tn12zhtgfmPjeAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get teams"},"description":"Get details of the teams the user is a member of. Limited to organizations accessible with current credentials","inputSchema":{"type":"object","properties":{"user":{"type":"string","description":"Username to get teams for. If not provided, uses the authenticated user."}}},"name":"get_teams","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACSUlEQVRIidWVv09TURTHP+e+2mJMEOLiQBig4GtN4WEHRQxGMfHHYkx0YpJJR+OmjMTNP0CjRv4DJyNETXTQODRQSkmJTweYBUw0KeX1HoeCKY3v0eKi3/Gdc74/zrs3F/53SJN9Jul6owhpAJQlvzT/HrB/LZB0h4YR+wjI7B6UBVW55ZfmPkY6a4J8FugQ1euOLbc7ttyuyDWLJhA7k0wNnNpvAulLefOKdiZM4BWLxbX6Yncm0xkPTB5lzS/lhwBtKUHS9c4qOiCqdxrJAVYKhXVE7iIMJt2h0TCe8BVt/1Cjm7OhPZXETK23mm5ZQMTGQom3YW0gACoS2hsqoNbJAwTm4FjocFtwEcCozYcajTBo+lLenEUTWzE7vFIorNcXXdc9EpCYB775pfwJQu5E1BqsVW6L8CoemHwy7d39vfN4+VJgeYhwGPRGGPleCQCkN+XdE3Tqz1W97y8tPIgkCCv092dc68gzkGFgHXiNsFrLJt2IjgGdwAfH6sTy8sJy0wK9xwbPieEFSlmRyY5DzvNcLrdV35PNZg9s/KzeFNEpII6aq35p7t2eAjXn5hOwqk718pfFxdXQ/EDP8Wy3scFLoMuxerIxSeMxFeuYpyjlZsgBvhZzK9bErgAVa+RJo+ldAn0p7wJwWpHJZsjrRVRlUuFMT3rgfEQCOw5stDlb082S70CCH9PAd2NlPFRAkRGEN8VisdKqgO/7mwJvEUZCBYCjwOdWyXdga7Nd9d9232SRCSo28oWKgjjVxxrElvY7/2/iF/Bu47CZ2fOnAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABaElEQVRIidWUvUrDcBTFz79YnRSii4N2cVB8AuvgByKuQqmrILQPoe9SfABxV5wUna2IVLq1k11adffn4A3UNPknjQ56IARyzz3ncj8i/Xe4LCSgIGlD0qp9epJ07Zz7+HEFQBl4YBRNoPwb4u9AB6gA0/bsAy3gDVjLK+6syg4wGxMPLHYPZGp1VGDLWlHxcKrG2UziFDwe4UAvPZyLCHcsgwlPLETYmkSuz6Bp7x0PZy/CzQ6gYENuAUFMfA7o2pB9hXpN1m0VOzbQGXsOTDz/mpqBA05ijizEcZpG4v4CK5IaksqS+pKuJHUtXNLXbAJJd5KOnHPP41S+DbwCL0ANKMZwikAd6AED3y2MVG7ij8BiBn7JuANgOY3sgFurPFU8YtIDbry/DWDXhlfLKj6UW7fc5LsBToE+MJnDYMra1PCR2sDZuOJD+efAt22KXuC8pHZeA8td8FVQBZIJKQCWgMO8+X8Tn12zhtgfmPjeAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get issue details"},"description":"Get information about a specific issue in a GitHub repository.","inputSchema":{"type":"object","properties":{"issue_number":{"type":"number","description":"The number of the issue"},"method":{"type":"string","description":"The read operation to perform on a single issue.\nOptions are:\n1. get - Get issue details. Also returns best-effort hierarchy flags (`has_parent`, `has_children`); `parent` and `sub_issues_summary` are optional relationship summaries.\n2. get_comments - Get issue comments.\n3. get_sub_issues - Get sub-issues (children) of the issue.\n4. get_parent - Get the parent issue, if this issue is a sub-issue of another.\n5. get_labels - Get labels assigned to the issue.\n","enum":["get","get_comments","get_sub_issues","get_parent","get_labels"]},"owner":{"type":"string","description":"The owner of the repository","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"The name of the repository","x-mcp-header":"repo"}},"required":["method","owner","repo","issue_number"]},"name":"issue_read","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Create or update issue/pull request"},"description":"Create a new or update an existing issue in a GitHub repository.","inputSchema":{"type":"object","properties":{"assignees":{"type":"array","items":{"type":"string"},"description":"Usernames to assign to this issue"},"body":{"type":"string","description":"Issue body content"},"duplicate_of":{"type":"number","description":"Issue number that this issue is a duplicate of. Only used when state_reason is 'duplicate'."},"issue_fields":{"type":"array","items":{"type":"object","properties":{"delete":{"type":"boolean","description":"Set to true to clear this field's current value on the issue. Cannot be combined with 'value' or 'field_option_name'.","enum":[true]},"field_name":{"type":"string","description":"Issue field name (case-insensitive). Must match a field returned by list_issue_fields for this repository or its organization."},"field_option_name":{"type":"string","description":"Option name for single-select fields. Validated against the field's options before the API call. Cannot be combined with 'value' or 'delete'."},"value":{"type":["string","number","boolean"],"description":"Value to set. Use for text, number, and date fields (date as YYYY-MM-DD). For single-select fields, prefer 'field_option_name' so the option is validated before the API call. Cannot be combined with 'field_option_name' or 'delete'."}},"required":["field_name"],"additionalProperties":false},"description":"Issue field values to set or clear. Each item requires 'field_name' and exactly one of 'value', 'field_option_name', or 'delete: true'."},"issue_number":{"type":"number","description":"Issue number to update"},"labels":{"type":"array","items":{"type":"string"},"description":"Labels to apply to this issue"},"method":{"type":"string","description":"Write operation to perform on a single issue.\nOptions are:\n- 'create' - creates a new issue.\n- 'update' - updates an existing issue.\n","enum":["create","update"]},"milestone":{"type":"number","description":"Milestone number"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"state":{"type":"string","description":"New state","enum":["open","closed"]},"state_reason":{"type":"string","description":"Reason for the state change. Ignored unless state is changed.","enum":["completed","not_planned","duplicate"]},"title":{"type":"string","description":"Issue title"},"type":{"type":"string","description":"Type of this issue. Only use if issue types are enabled for this repository. Use list_issue_types tool to get valid type values for this repository or its owner organization. If the repository doesn't support issue types, omit this parameter."}},"required":["method","owner","repo"]},"name":"issue_write","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List branches"},"description":"List branches in a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo"]},"name":"list_branches","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List commits"},"description":"Get list of commits of a branch in a GitHub repository. Returns at least 30 results per page by default, but can return more if specified using the perPage parameter (up to 100).","inputSchema":{"type":"object","properties":{"author":{"type":"string","description":"Author username or email address to filter commits by"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"path":{"type":"string","description":"Only commits containing this file path will be returned"},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"sha":{"type":"string","description":"Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch of the repository. If a commit SHA is provided, will list commits up to that SHA."},"since":{"type":"string","description":"Only commits after this date will be returned (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD)"},"until":{"type":"string","description":"Only commits before this date will be returned (ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DD)"}},"required":["owner","repo"]},"name":"list_commits","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List issue fields"},"description":"List issue fields for a repository or organization. Returns field definitions including name, type (text, number, date, single_select), and for single_select fields the list of valid option names. When repo is omitted, returns org-level fields directly.","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"The account owner of the repository or organization. The name is not case sensitive.","x-mcp-header":"owner"},"repo":{"type":"string","description":"The name of the repository. When provided, returns fields for this specific repository (inherited from its organization). When omitted, returns org-level fields directly.","x-mcp-header":"repo"}},"required":["owner"]},"name":"list_issue_fields","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List available issue types"},"description":"List supported issue types for a repository or its owner organization. When repo is omitted, returns org-level issue types directly.","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"The account owner of the repository or organization.","x-mcp-header":"owner"},"repo":{"type":"string","description":"The name of the repository. When provided, returns issue types for this specific repository. When omitted, returns org-level issue types directly.","x-mcp-header":"repo"}},"required":["owner"]},"name":"list_issue_types","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List issues"},"description":"List issues in a GitHub repository. For pagination, use the 'endCursor' from the previous response's 'pageInfo' in the 'after' parameter.","inputSchema":{"type":"object","properties":{"after":{"type":"string","description":"Cursor for pagination. Use the cursor from the previous response."},"direction":{"type":"string","description":"Order direction. If provided, the 'orderBy' also needs to be provided.","enum":["ASC","DESC"]},"field_filters":{"type":"array","items":{"type":"object","properties":{"field_name":{"type":"string","description":"Name of the custom field (e.g. \"Priority\"). Case-insensitive."},"value":{"type":"string","description":"Value to filter on. For single-select fields, the option name (e.g. \"P1\"). For dates, YYYY-MM-DD. For numbers, the numeric value as a string. For text, the text value."}},"required":["field_name","value"]},"description":"Filter by custom issue field values. Each entry takes a field_name and a value; the server looks up the field and coerces the value to its type (single-select option name, text, number, or YYYY-MM-DD date)."},"labels":{"type":"array","items":{"type":"string"},"description":"Filter by labels"},"orderBy":{"type":"string","description":"Order issues by field. If provided, the 'direction' also needs to be provided.","enum":["CREATED_AT","UPDATED_AT","COMMENTS"]},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"since":{"type":"string","description":"Filter by date (ISO 8601 timestamp)"},"state":{"type":"string","description":"Filter by state, by default both open and closed issues are returned when not provided","enum":["OPEN","CLOSED"]}},"required":["owner","repo"]},"name":"list_issues","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List pull requests"},"description":"List pull requests in a GitHub repository. If the user specifies an author, then DO NOT use this tool and use the search_pull_requests tool instead.","inputSchema":{"type":"object","properties":{"base":{"type":"string","description":"Filter by base branch"},"direction":{"type":"string","description":"Sort direction","enum":["asc","desc"]},"head":{"type":"string","description":"Filter by head user/org and branch"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"sort":{"type":"string","description":"Sort by","enum":["created","updated","popularity","long-running"]},"state":{"type":"string","description":"Filter by state","enum":["open","closed","all"]}},"required":["owner","repo"]},"name":"list_pull_requests","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List releases"},"description":"List releases in a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo"]},"name":"list_releases","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List repository collaborators"},"description":"List collaborators of a GitHub repository. Results are paginated; the response includes `nextPage`, `prevPage`, `firstPage`, and `lastPage` fields. To get the next page, use the `nextPage` value as the `page` parameter.","inputSchema":{"type":"object","properties":{"affiliation":{"type":"string","description":"Filter by affiliation. Can be one of: 'outside' (outside collaborators), 'direct' (all with permissions regardless of org membership), 'all' (all collaborators). Default: 'all'","enum":["outside","direct","all"]},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (default 1, min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (default 30, min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo"]},"name":"list_repository_collaborators","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"List tags"},"description":"List git tags in a GitHub repository","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo"]},"name":"list_tags","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Merge pull request"},"description":"Merge a pull request in a GitHub repository.","inputSchema":{"type":"object","properties":{"commit_message":{"type":"string","description":"Extra detail for merge commit"},"commit_title":{"type":"string","description":"Title for merge commit"},"merge_method":{"type":"string","description":"Merge method","enum":["merge","squash","rebase"]},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"pullNumber":{"type":"number","description":"Pull request number"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","pullNumber"]},"name":"merge_pull_request","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACeElEQVRIibWVTUhUYRSGn/e74+iiQih1F9Vcmj9sptylUVBYkO4jcNeuJBdFKxe1CYQokGrRKjCEdtmqwEVmtqomQWeiUdc2EBUtUufe0yLHn1KLGXtX5zvn4zz3vd8f/Gfp90Qs0drmpA6MT1EveDo1NfV92wB+KnMdo39Nfs4L7eSHD5Nz1QJcJYglWtsw+iUehAuRRjO1g+0KHLerbb4OIHnHAC1FdW129s3XmUJuwnBDoOPbA7BwHsD7QWq1HKYN5msBRCpB1AueLoSROSkciSUyj5ClhE6BLtYC8CpBqVRabNrdMmIiJdQjuUbQ1WI+d78WwIbykxnzU9np7ejlNq2YxQ4ebNtTKyCyWcEgYl55EDj/a7ihFEtkLkr0As2YxjwL+9aem00dCEYNzvnJzLDvH27aaM5y80HEnKGHKGwPnEbT6fSOvzpAmrDQnkncpC7siiUzz2QqIPu25iOuGBorTufO/AJmH0v2ajHwuoHhrQHATOH9rQPJ7IjDLgs6kZ0F6it1AzArVcZLdUE+WnYgmv/uYFmz+dxH4NJGNT+RfYLCE7F4tn0pGkxHy94AmBm8/GfAVvIs7AukUTkbj5YdYIbZ9WJh8m1lzrrbNB4/tD+QuyPsdCibF26gmM/dY/NdRDqd3rEYeN04mswYL+ZXm68DxOPxnWXXMClsp+GGhCWBTtClYj53t1qXK78oVH2XYB/mHZ0pvHsN4Cczzw3rBaoGrJ6D5ZUvN1i+kjI0LWiptjmscbC88hZZCAf2trZeq1v0UsJ6wF7UAlhxUMxPvkW6AboQLbvPcjaO+BIx11cL4I9H308eOiLRQUhpOx79/66fNKzrOCYNDm0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABjElEQVRIibWVPS/DURTGnysSC0HiZdWVrZ28JDaLT8BHaBsMdjqZJDXiAzC2LF5mX6GtATGiIsGARH+Gnj9X8a/kf3uWe3Py3Oc559xz75E6bK7VAWQkzUi6lXTonHsOpgYUgAZfdgmkQpFnjHwb6AemgDpQCiWwYlEPeL4i8JCEt8vb39g67vkmPH8yA3qt5nVgCzi1jLJBBEwkBZSAdxPKAj86LYQQQCU4cYvAKzDUSYF3YC+uRIAD8sA58ACU//VuTODE1n1g+A9c3jBH1tJ1a5TeCPNrdACSCpKeJG1IepN0LKkm6dGDrkqqOOdm7dyUpDNJi865PUnqjsvEObcJHEhaljQnaV5STwvszttXbR2J441KtB4LauLKVpZpYBDYte8mHUogZTWPrAGstTtQBl6AayDX7qHZD7AALMVGDvQBV5ZyETi2qHLtMvmXWRQAk57vBKgl4fV/0+jmq56vImk0icCnAWm7pB3riGngnlADx0TW+T4yL4CxJJy/Df20mkP/TqGHfifsA7INs3X5i3+yAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Get details for a single pull request"},"description":"Get information on a specific pull request in GitHub repository.","inputSchema":{"type":"object","properties":{"after":{"type":"string","description":"Cursor for pagination, used only by the get_review_comments method. Pass the endCursor from the previous page's PageInfo to fetch the next page."},"method":{"type":"string","description":"Action to specify what pull request data needs to be retrieved from GitHub. \nPossible options: \n 1. get - Get details of a specific pull request.\n 2. get_diff - Get the diff of a pull request.\n 3. get_status - Get combined commit status of a head commit in a pull request.\n 4. get_files - Get the list of files changed in a pull request. Use with pagination parameters to control the number of results returned.\n 5. get_commits - Get the list of commits on a pull request. Use with pagination parameters to control the number of results returned.\n 6. get_review_comments - Get review threads on a pull request. Each thread contains logically grouped review comments made on the same code location during pull request reviews. Returns threads with metadata (isResolved, isOutdated, isCollapsed) and their associated comments. Use cursor-based pagination (perPage, after) to control results.\n 7. get_reviews - Get the reviews on a pull request. When asked for review comments, use get_review_comments method. Use with pagination parameters to control the number of results returned.\n 8. get_comments - Get comments on a pull request. Use this if user doesn't specifically want review comments. Use with pagination parameters to control the number of results returned.\n 9. get_check_runs - Get check runs for the head commit of a pull request. Check runs are the individual CI/CD jobs and checks that run on the PR.\n","enum":["get","get_diff","get_status","get_files","get_commits","get_review_comments","get_reviews","get_comments","get_check_runs"]},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"pullNumber":{"type":"number","description":"Pull request number"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["method","owner","repo","pullNumber"]},"name":"pull_request_read","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Write operations (create, submit, delete) on pull request reviews"},"description":"Create and/or submit, delete review of a pull request.\n\nAvailable methods:\n- create: Create a new review of a pull request. If \"event\" parameter is provided, the review is submitted. If \"event\" is omitted, a pending review is created.\n- submit_pending: Submit an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request. The \"body\" and \"event\" parameters are used when submitting the review.\n- delete_pending: Delete an existing pending review of a pull request. This requires that a pending review exists for the current user on the specified pull request.\n- resolve_thread: Resolve a review thread. Requires only \"threadId\" parameter with the thread's node ID (e.g., PRRT_kwDOxxx). The owner, repo, and pullNumber parameters are not used for this method. Resolving an already-resolved thread is a no-op.\n- unresolve_thread: Unresolve a previously resolved review thread. Requires only \"threadId\" parameter. The owner, repo, and pullNumber parameters are not used for this method. Unresolving an already-unresolved thread is a no-op.\n","inputSchema":{"type":"object","properties":{"body":{"type":"string","description":"Review comment text"},"commitID":{"type":"string","description":"SHA of commit to review"},"event":{"type":"string","description":"Review action to perform.","enum":["APPROVE","REQUEST_CHANGES","COMMENT"]},"method":{"type":"string","description":"The write operation to perform on pull request review.","enum":["create","submit_pending","delete_pending","resolve_thread","unresolve_thread"]},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"pullNumber":{"type":"number","description":"Pull request number"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"threadId":{"type":"string","description":"The node ID of the review thread (e.g., PRRT_kwDOxxx). Required for resolve_thread and unresolve_thread methods. Get thread IDs from pull_request_read with method get_review_comments."}},"required":["method","owner","repo","pullNumber"]},"name":"pull_request_review_write","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Push files to repository"},"description":"Push multiple files to a GitHub repository in a single commit","inputSchema":{"type":"object","properties":{"branch":{"type":"string","description":"Branch to push to"},"files":{"type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"file content"},"path":{"type":"string","description":"path to the file"}},"required":["path","content"],"additionalProperties":false},"description":"Array of file objects to push, each object with path (string) and content (string)"},"message":{"type":"string","description":"Commit message"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","branch","files","message"]},"name":"push_files","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Request Copilot review"},"description":"Request a GitHub Copilot code review for a pull request. Use this for automated feedback on pull requests, usually before requesting a human reviewer.","inputSchema":{"type":"object","properties":{"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"pullNumber":{"type":"number","description":"Pull request number"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","pullNumber"]},"name":"request_copilot_review","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC20lEQVRIidWUS4wMURSGv3O7kWmPEMRrSMzcbl1dpqtmGuOxsCKECCKxEBusSJhIWEhsWLFAbC1sWFiISBARCyQ2kzSZGaMxHokgXvGIiMH0PRZjpJqqHpb+TeX+59z//H/q5sD/DqlX9H1/zFeX2qzIKoFWYDKgwBtUymL0UkNaT3V3d3/+5wG2EGxB9TDIxGFMvhVhb9/drpN/NaDJC7MGdwJk6TDCv0Gvq0lve9R762GUNdFDLleaZNBrICGq+4yhvf9TJtP/KZNB2PrLlbBliBfRhajuAwnFVa/n8/nkxFkv3GO9oJrzgwVxdesV71ov6I2r5fxggfWCatYL9yYmUJgLPH7Q29WZ4OED6Me4wuAdeQK6MMqna9t0GuibBHFAmgZ9JMG9BhkXZWoSCDSATIq7aguBD0wBplq/tZBgYDIwKnZAs99mFRYD9vd/YK0dpcqhobM6d9haWyOULRTbAauwuNlvsxHTYP3iBnVyXGAa8BIYC3oVeAKioCtAPEE7FCOgR0ErIJdBBZgNskzh40+NF6K6s+9e91lp9osrxMnFoTSmSmPVsF+E5cB0YEDgtoMjjypd5wCy+WC9GnajhEAa4bkqV9LOHKwa9/yneYeyUqwX3AdyQ5EeVrrqro/hYL0g+ggemKh4HGbPmVu0+fB8U76lpR6XgJwZpoGUpNYiusZg1tXjkmCAav0OMTXfJC4eVYPqwbot6l4BCPqyLhd7lwMAWC/cYb3gi/UCzRaKOxsbFzVEM1iv2Ebt5v2Dm14qZbJecZf1Ah3UCrcTbbB+awHnjgHLgHeinHYqZ8aPSXWWy+XvcQZLpdKI9/0D7UbZiLIJmABckVSqo+/OrUrNgF+D8q1LEdcBrAJGAJ8ROlGeicorABWdAswE5gOjge8CF8Ad66v03IjqJb75WS0tE0YOmNWqLBGReaAzgIkMLrt3oM9UpSzCzW9pd+FpT8/7JK3/Gz8Ao5X6wtwP7N4AAAAASUVORK5CYII=","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACCElEQVRIid2UPWsUYRSFn3dxWWJUkESiBgslFokfhehGiGClBBQx4h9IGlEh2ijYxh+gxEL/hIWwhYpF8KNZsFRJYdJEiUbjCkqisj4W+y6Mk5nd1U4PDMOce+45L3fmDvzXUDeo59WK+kb9rn5TF9R76jm1+2/NJ9QPtseSOv4nxrvVmQ6M05hRB9qZ98ZR1NRralntitdEwmw8wQ9HbS329rQKuKLW1XJO/aX6IqdWjr1Xk/y6lG4vMBdCqOacoZZ3uBBCVZ0HDrcK2AYs5ZkAuwBb1N8Dm5JEISXoAnqzOtU9QB+wVR3KCdgClDIr6kCc4c/0O1BLNnahiYpaSmmGY62e/JpCLJ4FpmmMaBHYCDwC5mmMZBQYBC7HnhvAK+B+fN4JHAM+R4+3wGQI4S7qaExtol+9o86pq+oX9Yk6ljjtGfVprK2qr9Xb6vaET109jjqb3Jac2XaM1PLNpok1Aep+G/+dfa24nADTX1EWTgOngLE2XCYKQL0DTfKex2WhXgCutxG9i/fFNlwWpgBQL6orcWyTaldToRbUA2pow61XL0WPFfXCb1HqkPowCj6q0+qIWsw7nlpUj6i31OXY+0AdbGpCRtNRGgt1AigCX4EqsJAYTR+wAzgEdAM/gApwM4TwOOm3JiARtBk4CYwAB4F+oIfGZi/HwOfAM6ASQviU5/Vv4xcBzmW2eT1nrQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Search code"},"description":"Fast and precise code search across ALL GitHub repositories using GitHub's native search engine. Best for finding exact symbols, functions, classes, or specific code patterns.","inputSchema":{"type":"object","properties":{"order":{"type":"string","description":"Sort order for results","enum":["asc","desc"]},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"query":{"type":"string","description":"Search query (GitHub code search REST). Implicit AND between terms; supports `OR`, `NOT`, and `\"quoted phrase\"` for exact match. Qualifiers: `repo:owner/repo`, `org:`, `user:`, `language:`, `path:dir` (prefix match), `filename:exact.ext`, `extension:`, `in:file`, `in:path`, `size:`, `is:archived`, `is:fork`. Max 256 chars. Examples: `WithContext language:go org:github`; `\"package main\" repo:o/r`; `func extension:go path:cmd repo:o/r`; `NOT TODO language:go repo:o/r`."},"sort":{"type":"string","description":"Sort field ('indexed' only)"}},"required":["query"]},"name":"search_code","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Search commits"},"description":"Search for commits across GitHub repositories using GitHub's commit search syntax. Useful for finding specific changes, authors, or messages across one or many repositories. Searches the default branch only.","inputSchema":{"type":"object","properties":{"order":{"type":"string","description":"Sort order","enum":["asc","desc"]},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"query":{"type":"string","description":"Commit search query (GitHub commit search REST). Searches commit messages on the default branch only. Scope the search with `repo:owner/repo`, `org:`, or `user:` (queries without a scope qualifier match across all of GitHub and are usually not what you want). Other qualifiers: `author:`, `committer:`, `author-name:`, `committer-name:`, `author-email:`, `committer-email:`, `author-date:`, `committer-date:` (supports `\u003e`, `\u003c`, `\u003e=`, `\u003c=`, and `YYYY-MM-DD..YYYY-MM-DD` ranges), `merge:true|false`, `hash:`, `tree:`, `parent:`, `is:public`. Examples: `repo:owner/repo fix panic`; `org:github author:defunkt committer-date:\u003e=2024-01-01`; `\"refactor cache\" repo:o/r`; `hash:abc1234 repo:o/r`."},"sort":{"type":"string","description":"Sort by author or committer date (defaults to best match)","enum":["author-date","committer-date"]}},"required":["query"]},"name":"search_commits","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Search issues"},"description":"Search for issues in GitHub repositories using issues search syntax already scoped to is:issue","inputSchema":{"type":"object","properties":{"order":{"type":"string","description":"Sort order","enum":["asc","desc"]},"owner":{"type":"string","description":"Optional repository owner. If provided with repo, only issues for this repository are listed.","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"query":{"type":"string","description":"Search query using GitHub issues search syntax"},"repo":{"type":"string","description":"Optional repository name. If provided with owner, only issues for this repository are listed.","x-mcp-header":"repo"},"sort":{"type":"string","description":"Sort field by number of matches of categories, defaults to best match","enum":["comments","reactions","reactions-+1","reactions--1","reactions-smile","reactions-thinking_face","reactions-heart","reactions-tada","interactions","created","updated"]}},"required":["query"]},"name":"search_issues","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Search pull requests"},"description":"Search for pull requests in GitHub repositories using issues search syntax already scoped to is:pr","inputSchema":{"type":"object","properties":{"order":{"type":"string","description":"Sort order","enum":["asc","desc"]},"owner":{"type":"string","description":"Optional repository owner. If provided with repo, only pull requests for this repository are listed.","x-mcp-header":"owner"},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"query":{"type":"string","description":"Search query using GitHub pull request search syntax"},"repo":{"type":"string","description":"Optional repository name. If provided with owner, only pull requests for this repository are listed.","x-mcp-header":"repo"},"sort":{"type":"string","description":"Sort field by number of matches of categories, defaults to best match","enum":["comments","reactions","reactions-+1","reactions--1","reactions-smile","reactions-thinking_face","reactions-heart","reactions-tada","interactions","created","updated"]}},"required":["query"]},"name":"search_pull_requests","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Search repositories"},"description":"Find GitHub repositories by name, description, readme, topics, or other metadata. Perfect for discovering projects, finding examples, or locating specific repositories across GitHub.","inputSchema":{"type":"object","properties":{"minimal_output":{"type":"boolean","description":"Return minimal repository information (default: true). When false, returns full GitHub API repository objects.","default":true},"order":{"type":"string","description":"Sort order","enum":["asc","desc"]},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"query":{"type":"string","description":"Repository search query. Examples: 'machine learning in:name stars:\u003e1000 language:python', 'topic:react', 'user:facebook'. Supports advanced search syntax for precise filtering."},"sort":{"type":"string","description":"Sort repositories by field, defaults to best match","enum":["stars","forks","help-wanted-issues","updated"]}},"required":["query"]},"name":"search_repositories","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABTklEQVRIie2UPUvDUBSGn5PW2qnYwUXExWhNpY2CUKT+BB10E3R0dnbrppuKLgouHbr5X5qlKRIFB+cuguBHj4O05KYfNsVJ+m7n3Hue54ZcrhDKQqGQTX1YVwg7QIbx0gh8b7VTJMMrqS+5RtgD7oDX2GhhC6UcbhkCVLZVuX1sesex4YCdX6uAGgIrsicjlrTGgQ9KVPDnmQgmgongPwgkXNiOq8ADMAtkx4UGvtflJvus20ANeIlN/vW5/kkt8L3D2HD6P9dRgSI8dQcc9w1IR3acBE3vbFSp8ZMVnoFSqJ/unZDe3pAYXyDIJarn9opbDZrewaAh2y7Oy5S1r6h5QG2Xxbw3piDw6xe244oKyxFmC5ihc+tS1qaqngKJyAEBGmZvSGzHVYT790T7aPozsaFoFZGboFGvDJsbOYuOuxuuc7n1uaV8sRSH8Q1DUVLnYLty3gAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA+ElEQVRIie2WLU5DQRRGzwUEirQCQ9BsoSGtZQHgSFgIsg4JXQCGP8V2QCBIEATZkJDgOJi+MJnMo30TFHmfmzvzfeeKyZ2BROpQvVHfrddDmhkZ4BY4Ai6BD7prAowjIoq7i85nFcGNf6qa1tayM1vAvBZQUg74c/WAHtAD/gMgn6YCT8A2MKwOTaZpCfAF3AGvFdlLx7XqdUVw4186rgWeE8Nn4cU67QLNAS/ASG3qmwVPqdaqjWw9A86BK+CkzaTuAseFBse/AiLiQg1gLzs3Bwb8XIp94AxYL/Af2xordap6v/htHKhv6nTlgBUAh9l6Rx11yfgG8ne/zwh2OysAAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":true,"title":"Search users"},"description":"Find GitHub users by username, real name, or other profile information. Useful for locating developers, contributors, or team members.","inputSchema":{"type":"object","properties":{"order":{"type":"string","description":"Sort order","enum":["asc","desc"]},"page":{"type":"number","description":"Page number for pagination (min 1)","minimum":1},"perPage":{"type":"number","description":"Results per page for pagination (min 1, max 100)","minimum":1,"maximum":100},"query":{"type":"string","description":"User search query. Examples: 'john smith', 'location:seattle', 'followers:\u003e100'. Search is automatically scoped to type:user."},"sort":{"type":"string","description":"Sort users by number of followers or repositories, or when the person joined GitHub.","enum":["followers","repositories","joined"]}},"required":["query"]},"name":"search_users","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAADWklEQVRIidWTT2hcVRTGf+e+mUn9Q3WMqbbWgJ1JnMw4meiAUkoxIRG1IKK4ELqoVkQXUsVuXbropoq4EWykuBDURV2I0r9GrC0VopnXTGaefYkhDahBOg4SnSTv3eMiTphMJ6Spq57dPd+933e/794DN3rJWkA+n49W54MB4AE1aoyawqVS1xn4PPzfAsnUg48iOgyaaIJ8I7r/5wn3u2sVMM2N7nTvbsQeB42J6nOOrW12bG2zIs8ohFblZHe6d/d1Oejv74/MzlU8lGibCfqKxeKVRrwzm43HAlMAatvviqdHRkaC9QScxkVk021DwAFRfalcGv+xeXN1bq52x5ats8Crf80vnL3yx29TiZ7e59s7tg7Ft3TEc5n09PT0tG08syoiEckBGF04seaVFtuOA1ixuf9OvQP6rqj5avb3SmlHKptdU0BhaT3L1gbLsVpRgMlSYdvNUb1VlKeAqBFzsjObjbcWMNYFCMxNg2sJmE3B4wCOmEK957ru/KVy4UtxnCeBO2OheaOONX9Tk+zJlRXsUsTunLl4sdIIplKp9oC2MYR//FJ3T6uZSKZzZ7Es+OXCIECkOQFVeUFET8UCU0im+w7WMydWeyKwHEboUCuDaw2cKGpFVx76qjmYLI+dRzkE3IvqZ0RrVaK1KsqnCNsROTTpjX3fijyReSip8LCo+aFlRN3d2ZR15COQnUAFOIVwedmbdCI6CMSBc47V/Z7neivkqWxexHwC2h4lmi2VRn9dJZC4Pzcghi9Qaoq8dfstztHR0dFVvyqfz0f/nA9fFNG3gRhqnvbLP30LkOzJfQP0Kzw7WSocW+Vg+ebmAjCjTrhncnz8cqsI6rUjk+80NvgauMex+ojnuV4ynTuMcgD4G/RNv+QO199ArGOGUWrXQg4wVRydCTF7gCVr5Agg/kThIE6YQjkH8mEi1bcPQLp6+h5T9AToy37JPbIeeWN1pXOvqPKBFR2amnBPA2QymdiidY4pMmhCEgbsXqDSZsKPN0IOoIvzR4GqsbK33isWi4vG8hogoaOvG0V2oXK6WCwublTA9/0FgTMIuxr7nuf+AowIDBjgboz6GyVfcSF4wLarAGEcuC8iyj4JuXC9Ak5o3g8j4fnmvpXIe44NWg7kjVX/Ap7dYx0LcmfJAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAB20lEQVRIidXUvUuVcRQH8PPc1JYUrKWUDCmwoKa2QsTKoKEwxWhr6G1tqDVqaAn8F6I5oS0ipBp6WZqiAi1bEgIrIpcorD4NHvFye+7Vrkv94Dc853zP93venl/E/36Keg60RsRgROyOiEpEPI+IB0VR/FyzKgYw48/zBv1rJe/HN7zDKNrzDmMqfc2JoAVvk3xjib8zfa/R0ozA4WzFaAPMWGKG8vskLuDIiqK4lMHtDTAdibmY3+9rZrSnGl+piV9YqcpY3jwREUVRdEXEhog4GhGtETGJznrZHchMhhtUcCIxh0p8u/ADV+sFV3KAU2VZYBNmE7OuDsdj3K+XYGAfvua2jGXPOzLz2VzT/Q3iH2GykUCByyU/2dK50iB2B77jWj3ATjxNos+4hfG8E2mDJ+irid2LaXzCljLyQcxjDmctvkW1mFacwwd8wUCV72GKH6+X+TxeYGvd/i3je/AqRfrSNo6F5DldDS6y5LnVkFfFbcPHHGqRtu24i184tQQcytLOrJa8SuR8xh6ssrXhTm5bd+BmDq+tCYH12aYbNfbe3KbrYfH9mPhb8iqy25gusd/Ds0pEbI6ImWYFImI6IrpK7C8jojcwgu5m2dGFYyX2How0y/vvnN8dpHfeBcHNQgAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Change sub-issue"},"description":"Add a sub-issue to a parent issue in a GitHub repository.","inputSchema":{"type":"object","properties":{"after_id":{"type":"number","description":"The ID of the sub-issue to be prioritized after (either after_id OR before_id should be specified)"},"before_id":{"type":"number","description":"The ID of the sub-issue to be prioritized before (either after_id OR before_id should be specified)"},"issue_number":{"type":"number","description":"The number of the parent issue"},"method":{"type":"string","description":"The action to perform on a single sub-issue\nOptions are:\n- 'add' - add a sub-issue to a parent issue in a GitHub repository.\n- 'remove' - remove a sub-issue from a parent issue in a GitHub repository.\n- 'reprioritize' - change the order of sub-issues within a parent issue in a GitHub repository. Use either 'after_id' or 'before_id' to specify the new position.\nWrites issue hierarchy. To move a sub-issue to a new parent, use `add` with `replace_parent=true`; there is no writable parent field.\n"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"replace_parent":{"type":"boolean","description":"When true, replaces the sub-issue's current parent issue. Use with 'add' method only."},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"sub_issue_id":{"type":"number","description":"The ID of the sub-issue to add. ID is not the same as issue number"}},"required":["method","owner","repo","issue_number","sub_issue_id"]},"name":"sub_issue_write","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAC2UlEQVRIicWVMUyTaRjHf/+vVUpOJOdilOYoUvVreq2CDmLOwdlAS5xMbrrhBifjYG7QjYuJw108nZx1huLgYlw0QshxChUK2koxHHcuCmgsFfieG0or3kGPSoz/8X3f5/97nvfN8z7wmaVqm9FodFfR/EnMugy5giCAwYwgI9Ff9Hl9L9Lp1zUBgsGO+rqGwnlhF4CdwBSyAUwvS1G220zHBSFgHtmVxYWvfp2ZGSj8LyAcjgfZphTQZtAr7OdsZvSP9RIJR+LtQhcNusF+d1aUfPp05M8NAavmg8AOk3cmN56+s1Hpa7U/cviUYbcw3jgex9ZCKoBgsKM+0PDuAdBq2He5zOiTzZiXtc+NxRw598GeLRcWTuTz+UUAp3ygrqFwHmgzeWdqNQd4PpFOy/gedNQfaDz3UQXRaHRX0fNPGdzNZUZO12q+VuHIoV7g5Hu/1/IinX7tABTNnwR2SvRsxRzAzOsBGuuWfAkoX5FZFzCVHR95tFVAbiI9DEwj6wLwl5YVMTRYLTDsxjuRrgGYYz/kxkbvbXRWMOBB24cKYI9ks1X8hXQDaAaa5XG9WjImZgV71wI+m8qAv8y0t1pSmP0ITANTmHO2qqvRZDALlTdgHKyjWkx2YvQ2cHtTacMxYBhWK5DoF4TCkXj7Jg02VKsbOwI0Y+qvALZrOQXMC13cKkDSJWBuJUCqAhgbG3uF7IpBcn/k8KlPNQ+78U5QQuhy/vHjuQoAYPndwi9gw4bd2ufGYrWatxz8No50ExhaKsz9Vl6vAPL5/KKzoiTGG0fOg1oqCbvxTp/juw/M+zynu/yTwjoD58CBQ02ez/pAR4E+M69ntf3/o1Y3dqR050oAQz7P6Z6cfPRRw647MkOhUMAfaDyH+AloBKYNPRT292rQHoMOSp09J3TZlt5ezWazxX97VR3638RiX9ct+RImSyBcrDT0ETMyMp4ptRIgVX7QL6J/ALSUEwJ5rdg2AAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABxElEQVRIibWVvW4TURCFv0vlNEDcIHAkKAERJOIKSjoUArwBPwUFFaKIIngAJARCPIgdh4cgRBYt6ZIAEYEqdhpEw0fhCbkKa68dxyNtsfNzzpnZu3NhwpYGBdUqcA+4A1wEZiK0DawD74FWSml3JAJ1CngGLAIngU1gFfgZKWeAG8AFoAu8At6mlH6VtqTOqJ/UP2pDnRuQO6c27VlbrQ0Dvq121Fulag7q5qPmW18SdSqUd9Qrw4Jn9bNR21YrRQkvYixDKy/AuB3jWjocqKpdtXFU8AxrOTqZzp2PgvnaMRDUA+tB7mypG+OCZ3hbahPgRPguAR9LihaicEu9WcKxClzOi/fU1wPAk7rjgX0uEfNG3cs7mJjtE+wA5/olpZQEHgNf6K2NJyW4NeD7v7c4WptjSc0svlMjdzyM2fbdOyOA7x/T+7mzGj9H8xgIWuquevpw4HmsivkxwBdC/WJRsBKLqqPOHgH8aqybtcJlF0m1WLndUToJ5V31q9r3NOYk7Wh1Wa0PyK3HzA3l/4H3uzIrwFNgCThF7/x/AH5EylngOnAe6AAvgXcppd9DEWRE08DdeIou/RVgJaXUGYQzUfsL+zmwV7BtIq0AAAAASUVORK5CYII=","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Edit pull request"},"description":"Update an existing pull request in a GitHub repository.","inputSchema":{"type":"object","properties":{"base":{"type":"string","description":"New base branch name"},"body":{"type":"string","description":"New description"},"draft":{"type":"boolean","description":"Mark pull request as draft (true) or ready for review (false)"},"maintainer_can_modify":{"type":"boolean","description":"Allow maintainer edits"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"pullNumber":{"type":"number","description":"Pull request number to update"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"},"reviewers":{"type":"array","items":{"type":"string"},"description":"GitHub usernames or ORG/team-slug team reviewers to request reviews from"},"state":{"type":"string","description":"New state","enum":["open","closed"]},"title":{"type":"string","description":"New title"}},"required":["owner","repo","pullNumber"]},"name":"update_pull_request","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]},{"annotations":{"idempotentHint":false,"readOnlyHint":false,"title":"Update pull request branch"},"description":"Update the branch of a pull request with the latest changes from the base branch.","inputSchema":{"type":"object","properties":{"expectedHeadSha":{"type":"string","description":"The expected SHA of the pull request's HEAD ref"},"owner":{"type":"string","description":"Repository owner","x-mcp-header":"owner"},"pullNumber":{"type":"number","description":"Pull request number"},"repo":{"type":"string","description":"Repository name","x-mcp-header":"repo"}},"required":["owner","repo","pullNumber"]},"name":"update_pull_request_branch","icons":[{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACwUlEQVRIie2Vz28UZRjHP993pi0QIC3YahNjirtmd3bS3Q1eUHvQEPUiEv8A4kXjwRJ78MCFBLjBBRKCHowHE38cNCbGGx6IUoKiodtNpoNmTJp4oSJNQ3pw29l5POxus2wo3QTwxPc0887zfD7zvu9kXnjEUfdNrjj5vJOmMP4e9JrfR1G02tuQD8tvgpck0dxCPwK30ViqnJTcr4bOmfRlI/PrhUJ5313woDpDpu8ss7f6nYHrvDnGcYlPsoY/bKaXwHY3HWfvgmNnMX0zvMM7069A3c3pkEYWa7UVgFxQPSfs7SSeH3k2rEy5jMubMBoG1yQ+SBbm53of+gCybMkk/H8VAFdbZisZLAFsJ11oyL+BUURcwrjWAZixXeIwxs/5UuVAr0QAYRjubGR+HWy3mb6QCIBXQe8nce0jgIkwfMo3/xLG085x8I9ofkMyUa0O+w2rgS0mcf3lboEDiKJo1cvsIDDr4D1DhTb8407hYhTdTJW+AvrdMnuhG9Je1m9BBzbfjXbyQcXypeqJLQt7+0rVE/mgYr3j7l7FDzOPBY8FDx6vc1EolPeNjI5/Jpgw7Lm9o+Pry//c/K0PhnLFyrSMDxE79jwxvn9079gvt28vrUD7V1EoFHalbltd2C7DfS4sAF4DTSdx7cL96LliZVriPPADuL+geRh0Z8il5SiKVn2ATENvCCYw78U/b8xdBcgHlYuGHQXuK5A4ClxM4vnXW8Lqp5JdWWt6h4CvWnsgxgDSbRZ3Gg0tCJ7sY4nGwDZOt/WBZtzN9FswLgM2sGann5mcPDaw5pWEHQH7cUu86SdkR3LF6tfrA814MNVpwNrM1leUxPXrSKfMeHcwdctyNotY8c3NbMX3LJsB3ZHsymDqlkHvYHYyievXWxPpSj4o75eYIuPWZof+vRKG4c61pncIx6gZsx34/5L/ACy3ElqUYhuvAAAAAElFTkSuQmCC","mimeType":"image/png","theme":"light"},{"src":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABn0lEQVRIie2Vvy5EURDGvyMUkhUU/pS2Q2clwvIKqL0Cm9ArrBcQShJPoLFR2YaIhIjQ7a7E39IiCgqVn8KsPY697E3oTHPumTPf990z986M9Mfm/A0wKGlMUlnSlnPuOQQAE5LOnXOFWErAIvBK1S6BZBAzZ2fzcckHjXwVaAXSwD2wWYN8A2iKK1ABt3m+ZeDRnseIthdgDxioxd1o662tfZIO7Lnf8xcklST1StqRdORxNEualHQIDDvnTmvdIGE5vwdWgLy93bQX0w0UgSdgKMC3AdfA7ndpSgKbduUbYBoI/7Ju4BiYrYFfAl4iBbxAgOyPgV9xWYDQ3xCXKK79C/wL/KJZoeW8QpsJCy0C54AMULaGmQu7sIAW4MpaxTKwbQU3U4dAxmLzwLpxXAIJP2jKgkY8Xx4o1SFwBmx7+7RxTUnVb9Bpa9HDFiR1/SRgWH+6FT3/h2qK6sBpB0aBB7yB880NcpaWtGHXjCsVBmb5PDIvgJ46BJKW84q9AguV87Adp/Q+9O8UMfQjRBKSxiV1SNp3zp3Ug/sVewPruexhKwhGXQAAAABJRU5ErkJggg==","mimeType":"image/png","theme":"dark"}]}]}} STDIN: {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"agent-session-id":"test-session-id","agent-tool-call-request-id":"test-id","progressToken":1},"name":"get_file_contents","arguments":{"owner":"block","repo":"goose","path":"README.md","sha":"ab62b863c1666232a67048b6c4e10007a2a5b83c"}}} STDOUT: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"successfully downloaded text file (SHA: 94c3fce7d230011289145bbdd9d45714280792ba)"},{"type":"resource","resource":{"uri":"repo://block/goose/sha/ab62b863c1666232a67048b6c4e10007a2a5b83c/contents/README.md","mimeType":"text/html; charset=utf-8","text":"\u003cdiv align=\"center\"\u003e\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n\u003cp align=\"center\"\u003e\n \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\n \u003cimg src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\"\u003e\n \u003c/a\u003e\n \u003ca href=\"https://discord.gg/7GaTvbDwga\"\u003e\n \u003cimg src=\"https://img.shields.io/discord/1287729918100246654?logo=discord\u0026logoColor=white\u0026label=Join+Us\u0026color=blueviolet\" alt=\"Discord\"\u003e\n \u003c/a\u003e\n \u003ca href=\"https://github.com/block/goose/actions/workflows/ci.yml\"\u003e\n \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\"\u003e\n \u003c/a\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n[![Watch the video](https://github.com/user-attachments/assets/ddc71240-3928-41b5-8210-626dfb28af7a)](https://youtu.be/D-DpDunrbpo)\n\n# Quick Links\n- [Quickstart](https://block.github.io/goose/docs/quickstart)\n- [Installation](https://block.github.io/goose/docs/getting-started/installation)\n- [Tutorials](https://block.github.io/goose/docs/category/tutorials)\n- [Documentation](https://block.github.io/goose/docs/category/getting-started)\n\n\n# a little goose humor ðŸĶĒ\n\n\u003e Why did the developer choose goose as their AI agent?\n\u003e \n\u003e Because it always helps them \"migrate\" their code to production! 🚀\n\n# goose around with us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@goose-oss)\n- [LinkedIn](https://www.linkedin.com/company/goose-oss)\n- [Twitter/X](https://x.com/goose_oss)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"}}]}} -STDERR: time=2026-07-29T10:52:54.580-04:00 level=INFO msg="server session disconnected" session_id="" +STDERR: time=2026-08-11T11:30:18.947-04:00 level=INFO msg="server session disconnected" session_id="" diff --git a/crates/goose/tests/mcp_replays/uvrun--withfastmcp==2.14.4fastmcpruntests_fastmcp_test_server.py b/crates/goose/tests/mcp_replays/uvrun--withfastmcp==2.14.4fastmcpruntests_fastmcp_test_server.py index 68636daa86..5d5d66e6fb 100644 --- a/crates/goose/tests/mcp_replays/uvrun--withfastmcp==2.14.4fastmcpruntests_fastmcp_test_server.py +++ b/crates/goose/tests/mcp_replays/uvrun--withfastmcp==2.14.4fastmcpruntests_fastmcp_test_server.py @@ -1,6 +1,6 @@ STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}},"roots":{},"sampling":{},"elicitation":{}},"clientInfo":{"name":"goose-desktop","version":"0.0.0"}}} STDERR: warning: The `native-tls` setting is deprecated and will be removed in a future release. Use `system-certs` instead. -STDERR: /Users/jackamadeo/.cache/uv/archive-v0/qVA6xesA-RY1n0SG/lib/python3.14/site-packages/fastmcp/server/auth/providers/jwt.py:10: AuthlibDeprecationWarning: authlib.jose module is deprecated, please use joserfc instead. +STDERR: /Users/jackamadeo/.cache/uv/archive-v0/al1Eg6tdenSv08iE/lib/python3.14/site-packages/fastmcp/server/auth/providers/jwt.py:10: AuthlibDeprecationWarning: authlib.jose module is deprecated, please use joserfc instead. STDERR: It will be compatible before version 2.0.0. STDERR: from authlib.jose import JsonWebKey, JsonWebToken STDERR: @@ -25,14 +25,14 @@ STDERR: │ âœĻ FastMCP 3.0 is coming! STDERR: │ Pin `fastmcp < 3` in production, then upgrade when you're ready. │ STDERR: ╰──────────────────────────────────────────────────────────────────────────────â•Ŋ STDERR: ╭──────────────────────────────────────────────────────────────────────────────â•Ū -STDERR: │ 🎉 Update available: 3.4.5 │ +STDERR: │ 🎉 Update available: 3.4.7 │ STDERR: │ Run: pip install --upgrade fastmcp │ STDERR: ╰──────────────────────────────────────────────────────────────────────────────â•Ŋ STDERR: STDERR: -STDERR: [07/29/26 10:52:55] INFO Starting MCP server 'mymcp' with server.py:2506 +STDERR: [08/11/26 11:30:18] INFO Starting MCP server 'mymcp' with server.py:2506 STDERR: transport 'stdio' -STDERR: /Users/jackamadeo/.cache/uv/archive-v0/qVA6xesA-RY1n0SG/lib/python3.14/site-packages/redis/asyncio/connection.py:1628: DeprecationWarning: FakeConnection is deprecated. Use FakeAsyncRedisConnection instead +STDERR: /Users/jackamadeo/.cache/uv/archive-v0/al1Eg6tdenSv08iE/lib/python3.14/site-packages/redis/asyncio/connection.py:2861: DeprecationWarning: FakeConnection is deprecated. Use FakeAsyncRedisConnection instead STDERR: return self.connection_class(**self.connection_kwargs) STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-11-25","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":true},"tasks":{"list":{},"cancel":{},"requests":{"tools":{"call":{}},"prompts":{"get":{}},"resources":{"read":{}}}}},"serverInfo":{"name":"mymcp","version":"2.14.4"}}} STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}