From 86fbf09255de4bae4ca6ea8edbe384986b4bb2e9 Mon Sep 17 00:00:00 2001 From: Sam Morrow Date: Fri, 2 Oct 2026 17:44:44 +0200 Subject: [PATCH] refactor(git,gists): type repository tree and gist MCP tools Expose concrete output schemas and structured content for get_repository_tree, list_gists, get_gist, create_gist and update_gist on supported protocol versions while preserving legacy arguments, error text, pagination, IFC labels and scopes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- pkg/github/__toolsnaps__/create_gist.snap | 18 +- .../__toolsnaps__/create_gist_typed.snap | 63 ++ pkg/github/__toolsnaps__/get_gist.snap | 629 +++++++++++++++- pkg/github/__toolsnaps__/get_gist_typed.snap | 660 +++++++++++++++++ .../__toolsnaps__/get_repository_tree.snap | 76 +- .../get_repository_tree_typed.snap | 127 ++++ pkg/github/__toolsnaps__/list_gists.snap | 638 +++++++++++++++- .../__toolsnaps__/list_gists_typed.snap | 681 ++++++++++++++++++ pkg/github/__toolsnaps__/update_gist.snap | 18 +- .../__toolsnaps__/update_gist_typed.snap | 63 ++ pkg/github/gists.go | 40 +- pkg/github/git.go | 10 +- pkg/github/git_gist_types.go | 142 ++++ pkg/github/typed_git_gist_outputs_test.go | 233 ++++++ 14 files changed, 3373 insertions(+), 25 deletions(-) create mode 100644 pkg/github/__toolsnaps__/create_gist_typed.snap create mode 100644 pkg/github/__toolsnaps__/get_gist_typed.snap create mode 100644 pkg/github/__toolsnaps__/get_repository_tree_typed.snap create mode 100644 pkg/github/__toolsnaps__/list_gists_typed.snap create mode 100644 pkg/github/__toolsnaps__/update_gist_typed.snap create mode 100644 pkg/github/git_gist_types.go create mode 100644 pkg/github/typed_git_gist_outputs_test.go diff --git a/pkg/github/__toolsnaps__/create_gist.snap b/pkg/github/__toolsnaps__/create_gist.snap index f91810bd00..ab94493f61 100644 --- a/pkg/github/__toolsnaps__/create_gist.snap +++ b/pkg/github/__toolsnaps__/create_gist.snap @@ -31,5 +31,21 @@ ], "type": "object" }, - "name": "create_gist" + "name": "create_gist", + "outputSchema": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "url" + ], + "type": "object" + } } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/create_gist_typed.snap b/pkg/github/__toolsnaps__/create_gist_typed.snap new file mode 100644 index 0000000000..11fbe70552 --- /dev/null +++ b/pkg/github/__toolsnaps__/create_gist_typed.snap @@ -0,0 +1,63 @@ +{ + "annotations": { + "idempotentHint": false, + "readOnlyHint": false, + "title": "Create Gist" + }, + "description": "Create a new gist", + "icons": [ + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAAD2ElEQVRIie2WS2xUVRjHf9/tHSgjRCA+C5am3gFmhnIHJypEY4aVujAYfCUGF8SFRhONgiRsiInxFRIiGhcSxQVEhYhhRQyycuMjUWaG0mnLLQ9BUORRiK20nTl/F1DSDJe2VOLC+F/dc873/c/vfDfnAf/rPyIb2Qgy4SI5njNjKTAb8BG9GBHSbmfeZwcrxQP/KlgqnVsj9CZQM9jlYL+JIYxbgEXAYrBDUaUYABQKBf/Yb2dXy6yzp1Lceb3BfIAgE66Q9C7wcw1v+aHK3iP1gXPnhrOcr6XD7eO/nytgvG2oD5g6kclbs+H9npgbdZQ2XwHWlM8n6a+uB51ucA2PRF17j8eZdHeXfgW2Drfd0KQflRj43OT2TQQKwHN8KjgBXAl2Q9/QMpndZti6rqtAxSmKfjgPPD1RqLHkyexhAM9p+/X2bm5rm9HS0tI4kWQL0uFeYE5UKc281uQgHR6W+Lqns/T85b5MuAjHWxgFYBjqDLCnP+mvnHHhQuNALbEK00Nc3FQDGCP/1Nqoo7TdB5qBQxNZFTDdjORwozWdSyF9i8dJnN4R3nHPc1PkvFsxd58/ODh5aKjRk1c7guwjQ29gnJez9cMeDa5Whou7cipwPm7WVCZc6ZwlRvb5ct90dZVjF+IZKxG+fC3pKZdPjrKgTQBBOnxNcKKns7ipPsAHDENx2RIbzTRtZF+tgSe4WoWlOxCnxoAalzygz1z8OVSdbM2Dvps56LuZJh4b081UwZgVpHMvNOXzyTHjR5EPHJXREjd4uFjsHf4O5od/jmWW9NnYP6R7gA+T/dUP7kyHvxi0Y/aTvOonPe3tR8cL5gEdwM3z5+di4a5F5XK5L6qUH63htZj0JLAFVENaa7WGfakFd6XH6+Vj7EY8VUXLgQ3/FA7g0pV2BNgBEKQXLgb7Tq72LLB6PB5edZJ9BZzFeLUll5t+PcDqFVXK3wMXJGuqHzPDiwU7XCz2mul1YFZiQF9ms9lrOGhVpe7pFKdUuu0BoNGM7rqhMxKt+Xw+UZ/jAxzoKL8fZMI5Eq8MOL8rSIfbwDokqoamGna7TA/WJ5vsoIx7U9lc7sD+YjGVWfiSZHcbdGP84SDhwQKJFcApBt3HdRZfGGw491d1R5AJt+LojTpLuy+DAUQdpVWtmYW7GmQvClaAbrRLtXDohGFFYHM1YXsu1wtbg7RFTquAZ5DXI/Q4sAwxzQDBWUw78dy6qNJ+bCRVVCm9F6RzkvSywTZMp4GbRi1/Uz6fbG5rm1EoFPxRA4G4i7opn0/OmzdvWlx8nGbPXjIlm81O6F33vwD+Bhvyhr7wtSBQAAAAAElFTkSuQmCC", + "theme": "light" + }, + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAACoElEQVRIie2WX2hXZRjHP8/4hW5UzoGFja2wdiF00S7EotAgIcKbMBoIu7HuuhANC3aRCTIQgmBBsEZ3s8hoIwomrLuB4E0YhqWFrjHY0NY2rGHh6uPFecXj2flt+/2mXoRfOHDO+z5/Ps973vO8B+7r/yi1U+1XL6gL6j/qZfWU+r7aca9YIgf1LtAL/AuMAOeA68AjQCfwLDAeEU8l+wpwCDgfEV/fFTq120zfq49XsWlVu3PPu5LPX2vI+4L6RrXJJnVanVEfqyHow+rnas8awH5Vx6pN7k2Vv1dvgnq1HFgD8Eq6//IOJ21QN6rr6w1wRp2t0/c3tb8w1qmeVK95S3+oJ9K2aVF7037+L9ldzF1dABWgHRivqypoBppyUB3AGHAFOAZMAY3Ao8DzwDqytzQBfAIcBa4CH+Rinr0J9mCaLFuRfcADheHvIqJaIftSzOci4soyBQ2k+O8A0xExUDSokPUyqwToAx4qjL1O9RVuA2ZWgFqVGoAFslUrUzvQkq7XVhHvZ6BVfUttWtF6BbBJ4ImyyYiYj4i5iJgDVtNI+4BvgI+BP9Vx9Vv1iNpWK9hPwCa1FK4WRcRCRLxKVmgXMEh2xPUAP6pbawEbTfd71gqWA5yIiKGIOJxAdwIbgDdrARsG5oC31eY7BVcAPQ38DZQdeQ2lYBExDxwBWoGv1JYaci6S+0OpJnUHsB74pTA1C2xRiy2JCkBEfJT+Kg4CF9QTZHtvkeyL3Qy8XJLzErBdfSYiflD3A9sSwO9kPfBpoBuYAT4t+H8BfAgMqceB+YgYpSj1JXVYnfd2Takj6oH861ZfVCfVwfS8Wx1Tr+Z8Z9XP1CdL8kWKOZ5sZ0qKX+LUlA7hyipslxzUyb/YnJeL0ahW66f3taJuAAWd129KkzycAAAAAElFTkSuQmCC", + "theme": "dark" + } + ], + "inputSchema": { + "properties": { + "content": { + "description": "Content for simple single-file gist creation", + "type": "string" + }, + "description": { + "description": "Description of the gist", + "type": "string" + }, + "filename": { + "description": "Filename for simple single-file gist creation", + "type": "string" + }, + "public": { + "default": false, + "description": "Whether the gist is public", + "type": "boolean" + } + }, + "required": [ + "filename", + "content" + ], + "type": "object" + }, + "name": "create_gist", + "outputSchema": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "url" + ], + "type": "object" + } +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/get_gist.snap b/pkg/github/__toolsnaps__/get_gist.snap index b4c053b549..5734b76c99 100644 --- a/pkg/github/__toolsnaps__/get_gist.snap +++ b/pkg/github/__toolsnaps__/get_gist.snap @@ -17,5 +17,632 @@ ], "type": "object" }, - "name": "get_gist" + "name": "get_gist", + "outputSchema": { + "$defs": { + "team": { + "additionalProperties": false, + "properties": { + "access_source": { + "type": "string" + }, + "assignment": { + "type": "string" + }, + "description": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ldap_dn": { + "type": "string" + }, + "members_count": { + "type": "integer" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_setting": { + "type": "string" + }, + "organization": { + "additionalProperties": false, + "properties": { + "advanced_security_enabled_for_new_repositories": { + "type": "boolean" + }, + "archived_at": { + "format": "date-time", + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "billing_email": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "default_repository_branch": { + "type": "string" + }, + "default_repository_permission": { + "type": "string" + }, + "default_repository_settings": { + "type": "string" + }, + "dependabot_alerts_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependabot_security_updates_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependency_graph_enabled_for_new_repositories": { + "type": "boolean" + }, + "deploy_keys_enabled_for_repositories": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "display_commenter_full_name_setting_enabled": { + "type": "boolean" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "following": { + "type": "integer" + }, + "has_organization_projects": { + "type": "boolean" + }, + "has_repository_projects": { + "type": "boolean" + }, + "hooks_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_verified": { + "type": "boolean" + }, + "issues_url": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "members_allowed_repository_creation_type": { + "type": "string" + }, + "members_can_change_repo_visibility": { + "type": "boolean" + }, + "members_can_create_internal_repositories": { + "type": "boolean" + }, + "members_can_create_pages": { + "type": "boolean" + }, + "members_can_create_private_pages": { + "type": "boolean" + }, + "members_can_create_private_repositories": { + "type": "boolean" + }, + "members_can_create_public_pages": { + "type": "boolean" + }, + "members_can_create_public_repositories": { + "type": "boolean" + }, + "members_can_create_repositories": { + "type": "boolean" + }, + "members_can_create_teams": { + "type": "boolean" + }, + "members_can_delete_issues": { + "type": "boolean" + }, + "members_can_delete_repositories": { + "type": "boolean" + }, + "members_can_fork_private_repositories": { + "type": "boolean" + }, + "members_can_invite_outside_collaborators": { + "type": "boolean" + }, + "members_can_view_dependency_insights": { + "type": "boolean" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_members_url": { + "type": "string" + }, + "public_repos": { + "type": "integer" + }, + "readers_can_create_discussions": { + "type": "boolean" + }, + "repos_url": { + "type": "string" + }, + "secret_scanning_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_push_protection_custom_link": { + "type": "string" + }, + "secret_scanning_push_protection_custom_link_enabled": { + "type": "boolean" + }, + "secret_scanning_push_protection_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_validity_checks_enabled": { + "type": "boolean" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_requirement_enabled": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "web_commit_signoff_required": { + "type": "boolean" + } + }, + "type": "object" + }, + "parent": { + "$ref": "#/$defs/team", + "type": "object" + }, + "permission": { + "type": "string" + }, + "permissions": { + "additionalProperties": { + "type": "boolean" + }, + "type": "object" + }, + "privacy": { + "type": "string" + }, + "repos_count": { + "type": "integer" + }, + "repositories_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "object", + "null" + ] + } + }, + "additionalProperties": false, + "properties": { + "comments": { + "type": "integer" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "description": { + "type": "string" + }, + "files": { + "additionalProperties": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "language": { + "type": "string" + }, + "raw_url": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "type": "object" + }, + "git_pull_url": { + "type": "string" + }, + "git_push_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owner": { + "additionalProperties": false, + "properties": { + "assignment": { + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "business_plus": { + "type": "boolean" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "followers_url": { + "type": "string" + }, + "following": { + "type": "integer" + }, + "following_url": { + "type": "string" + }, + "gists_url": { + "type": "string" + }, + "gravatar_id": { + "type": "string" + }, + "hireable": { + "type": "boolean" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "inherited": { + "type": "boolean" + }, + "inherited_from": { + "items": { + "$ref": "#/$defs/team", + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "ldap_dn": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_email": { + "type": "string" + }, + "organizations_url": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "permissions": { + "additionalProperties": false, + "properties": { + "admin": { + "type": "boolean" + }, + "maintain": { + "type": "boolean" + }, + "pull": { + "type": "boolean" + }, + "push": { + "type": "boolean" + }, + "triage": { + "type": "boolean" + } + }, + "type": "object" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_repos": { + "type": "integer" + }, + "received_events_url": { + "type": "string" + }, + "repos_url": { + "type": "string" + }, + "role": { + "type": "string" + }, + "role_name": { + "type": "string" + }, + "site_admin": { + "type": "boolean" + }, + "starred_url": { + "type": "string" + }, + "subscriptions_url": { + "type": "string" + }, + "suspended_at": { + "format": "date-time", + "type": "string" + }, + "text_matches": { + "items": { + "additionalProperties": false, + "properties": { + "fragment": { + "type": "string" + }, + "matches": { + "items": { + "additionalProperties": false, + "properties": { + "indices": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "text": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "object_type": { + "type": "string" + }, + "object_url": { + "type": "string" + }, + "property": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_authentication": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "user_view_type": { + "type": "string" + } + }, + "type": "object" + }, + "public": { + "type": "boolean" + }, + "updated_at": { + "format": "date-time", + "type": "string" + } + }, + "type": "object" + } } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/get_gist_typed.snap b/pkg/github/__toolsnaps__/get_gist_typed.snap new file mode 100644 index 0000000000..75a3b94659 --- /dev/null +++ b/pkg/github/__toolsnaps__/get_gist_typed.snap @@ -0,0 +1,660 @@ +{ + "annotations": { + "idempotentHint": false, + "readOnlyHint": true, + "title": "Get Gist Content" + }, + "description": "Get gist content of a particular gist, by gist ID", + "icons": [ + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAAD2ElEQVRIie2WS2xUVRjHf9/tHSgjRCA+C5am3gFmhnIHJypEY4aVujAYfCUGF8SFRhONgiRsiInxFRIiGhcSxQVEhYhhRQyycuMjUWaG0mnLLQ9BUORRiK20nTl/F1DSDJe2VOLC+F/dc873/c/vfDfnAf/rPyIb2Qgy4SI5njNjKTAb8BG9GBHSbmfeZwcrxQP/KlgqnVsj9CZQM9jlYL+JIYxbgEXAYrBDUaUYABQKBf/Yb2dXy6yzp1Lceb3BfIAgE66Q9C7wcw1v+aHK3iP1gXPnhrOcr6XD7eO/nytgvG2oD5g6kclbs+H9npgbdZQ2XwHWlM8n6a+uB51ucA2PRF17j8eZdHeXfgW2Drfd0KQflRj43OT2TQQKwHN8KjgBXAl2Q9/QMpndZti6rqtAxSmKfjgPPD1RqLHkyexhAM9p+/X2bm5rm9HS0tI4kWQL0uFeYE5UKc281uQgHR6W+Lqns/T85b5MuAjHWxgFYBjqDLCnP+mvnHHhQuNALbEK00Nc3FQDGCP/1Nqoo7TdB5qBQxNZFTDdjORwozWdSyF9i8dJnN4R3nHPc1PkvFsxd58/ODh5aKjRk1c7guwjQ29gnJez9cMeDa5Whou7cipwPm7WVCZc6ZwlRvb5ct90dZVjF+IZKxG+fC3pKZdPjrKgTQBBOnxNcKKns7ipPsAHDENx2RIbzTRtZF+tgSe4WoWlOxCnxoAalzygz1z8OVSdbM2Dvps56LuZJh4b081UwZgVpHMvNOXzyTHjR5EPHJXREjd4uFjsHf4O5od/jmWW9NnYP6R7gA+T/dUP7kyHvxi0Y/aTvOonPe3tR8cL5gEdwM3z5+di4a5F5XK5L6qUH63htZj0JLAFVENaa7WGfakFd6XH6+Vj7EY8VUXLgQ3/FA7g0pV2BNgBEKQXLgb7Tq72LLB6PB5edZJ9BZzFeLUll5t+PcDqFVXK3wMXJGuqHzPDiwU7XCz2mul1YFZiQF9ms9lrOGhVpe7pFKdUuu0BoNGM7rqhMxKt+Xw+UZ/jAxzoKL8fZMI5Eq8MOL8rSIfbwDokqoamGna7TA/WJ5vsoIx7U9lc7sD+YjGVWfiSZHcbdGP84SDhwQKJFcApBt3HdRZfGGw491d1R5AJt+LojTpLuy+DAUQdpVWtmYW7GmQvClaAbrRLtXDohGFFYHM1YXsu1wtbg7RFTquAZ5DXI/Q4sAwxzQDBWUw78dy6qNJ+bCRVVCm9F6RzkvSywTZMp4GbRi1/Uz6fbG5rm1EoFPxRA4G4i7opn0/OmzdvWlx8nGbPXjIlm81O6F33vwD+Bhvyhr7wtSBQAAAAAElFTkSuQmCC", + "theme": "light" + }, + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAACoElEQVRIie2WX2hXZRjHP8/4hW5UzoGFja2wdiF00S7EotAgIcKbMBoIu7HuuhANC3aRCTIQgmBBsEZ3s8hoIwomrLuB4E0YhqWFrjHY0NY2rGHh6uPFecXj2flt+/2mXoRfOHDO+z5/Ps973vO8B+7r/yi1U+1XL6gL6j/qZfWU+r7aca9YIgf1LtAL/AuMAOeA68AjQCfwLDAeEU8l+wpwCDgfEV/fFTq120zfq49XsWlVu3PPu5LPX2vI+4L6RrXJJnVanVEfqyHow+rnas8awH5Vx6pN7k2Vv1dvgnq1HFgD8Eq6//IOJ21QN6rr6w1wRp2t0/c3tb8w1qmeVK95S3+oJ9K2aVF7037+L9ldzF1dABWgHRivqypoBppyUB3AGHAFOAZMAY3Ao8DzwDqytzQBfAIcBa4CH+Rinr0J9mCaLFuRfcADheHvIqJaIftSzOci4soyBQ2k+O8A0xExUDSokPUyqwToAx4qjL1O9RVuA2ZWgFqVGoAFslUrUzvQkq7XVhHvZ6BVfUttWtF6BbBJ4ImyyYiYj4i5iJgDVtNI+4BvgI+BP9Vx9Vv1iNpWK9hPwCa1FK4WRcRCRLxKVmgXMEh2xPUAP6pbawEbTfd71gqWA5yIiKGIOJxAdwIbgDdrARsG5oC31eY7BVcAPQ38DZQdeQ2lYBExDxwBWoGv1JYaci6S+0OpJnUHsB74pTA1C2xRiy2JCkBEfJT+Kg4CF9QTZHtvkeyL3Qy8XJLzErBdfSYiflD3A9sSwO9kPfBpoBuYAT4t+H8BfAgMqceB+YgYpSj1JXVYnfd2Takj6oH861ZfVCfVwfS8Wx1Tr+Z8Z9XP1CdL8kWKOZ5sZ0qKX+LUlA7hyipslxzUyb/YnJeL0ahW66f3taJuAAWd129KkzycAAAAAElFTkSuQmCC", + "theme": "dark" + } + ], + "inputSchema": { + "properties": { + "gist_id": { + "description": "The ID of the gist", + "type": "string" + } + }, + "required": [ + "gist_id" + ], + "type": "object" + }, + "name": "get_gist", + "outputSchema": { + "$defs": { + "team": { + "additionalProperties": false, + "properties": { + "access_source": { + "type": "string" + }, + "assignment": { + "type": "string" + }, + "description": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ldap_dn": { + "type": "string" + }, + "members_count": { + "type": "integer" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_setting": { + "type": "string" + }, + "organization": { + "additionalProperties": false, + "properties": { + "advanced_security_enabled_for_new_repositories": { + "type": "boolean" + }, + "archived_at": { + "format": "date-time", + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "billing_email": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "default_repository_branch": { + "type": "string" + }, + "default_repository_permission": { + "type": "string" + }, + "default_repository_settings": { + "type": "string" + }, + "dependabot_alerts_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependabot_security_updates_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependency_graph_enabled_for_new_repositories": { + "type": "boolean" + }, + "deploy_keys_enabled_for_repositories": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "display_commenter_full_name_setting_enabled": { + "type": "boolean" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "following": { + "type": "integer" + }, + "has_organization_projects": { + "type": "boolean" + }, + "has_repository_projects": { + "type": "boolean" + }, + "hooks_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_verified": { + "type": "boolean" + }, + "issues_url": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "members_allowed_repository_creation_type": { + "type": "string" + }, + "members_can_change_repo_visibility": { + "type": "boolean" + }, + "members_can_create_internal_repositories": { + "type": "boolean" + }, + "members_can_create_pages": { + "type": "boolean" + }, + "members_can_create_private_pages": { + "type": "boolean" + }, + "members_can_create_private_repositories": { + "type": "boolean" + }, + "members_can_create_public_pages": { + "type": "boolean" + }, + "members_can_create_public_repositories": { + "type": "boolean" + }, + "members_can_create_repositories": { + "type": "boolean" + }, + "members_can_create_teams": { + "type": "boolean" + }, + "members_can_delete_issues": { + "type": "boolean" + }, + "members_can_delete_repositories": { + "type": "boolean" + }, + "members_can_fork_private_repositories": { + "type": "boolean" + }, + "members_can_invite_outside_collaborators": { + "type": "boolean" + }, + "members_can_view_dependency_insights": { + "type": "boolean" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_members_url": { + "type": "string" + }, + "public_repos": { + "type": "integer" + }, + "readers_can_create_discussions": { + "type": "boolean" + }, + "repos_url": { + "type": "string" + }, + "secret_scanning_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_push_protection_custom_link": { + "type": "string" + }, + "secret_scanning_push_protection_custom_link_enabled": { + "type": "boolean" + }, + "secret_scanning_push_protection_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_validity_checks_enabled": { + "type": "boolean" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_requirement_enabled": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "web_commit_signoff_required": { + "type": "boolean" + } + }, + "type": "object" + }, + "parent": { + "$ref": "#/$defs/team", + "type": "object" + }, + "permission": { + "type": "string" + }, + "permissions": { + "additionalProperties": { + "type": "boolean" + }, + "type": "object" + }, + "privacy": { + "type": "string" + }, + "repos_count": { + "type": "integer" + }, + "repositories_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "object", + "null" + ] + } + }, + "additionalProperties": false, + "properties": { + "comments": { + "type": "integer" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "description": { + "type": "string" + }, + "files": { + "additionalProperties": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "language": { + "type": "string" + }, + "raw_url": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "type": "object" + }, + "git_pull_url": { + "type": "string" + }, + "git_push_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owner": { + "additionalProperties": false, + "properties": { + "assignment": { + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "business_plus": { + "type": "boolean" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "followers_url": { + "type": "string" + }, + "following": { + "type": "integer" + }, + "following_url": { + "type": "string" + }, + "gists_url": { + "type": "string" + }, + "gravatar_id": { + "type": "string" + }, + "hireable": { + "type": "boolean" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "inherited": { + "type": "boolean" + }, + "inherited_from": { + "items": { + "$ref": "#/$defs/team", + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "ldap_dn": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_email": { + "type": "string" + }, + "organizations_url": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "permissions": { + "additionalProperties": false, + "properties": { + "admin": { + "type": "boolean" + }, + "maintain": { + "type": "boolean" + }, + "pull": { + "type": "boolean" + }, + "push": { + "type": "boolean" + }, + "triage": { + "type": "boolean" + } + }, + "type": "object" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_repos": { + "type": "integer" + }, + "received_events_url": { + "type": "string" + }, + "repos_url": { + "type": "string" + }, + "role": { + "type": "string" + }, + "role_name": { + "type": "string" + }, + "site_admin": { + "type": "boolean" + }, + "starred_url": { + "type": "string" + }, + "subscriptions_url": { + "type": "string" + }, + "suspended_at": { + "format": "date-time", + "type": "string" + }, + "text_matches": { + "items": { + "additionalProperties": false, + "properties": { + "fragment": { + "type": "string" + }, + "matches": { + "items": { + "additionalProperties": false, + "properties": { + "indices": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "text": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "object_type": { + "type": "string" + }, + "object_url": { + "type": "string" + }, + "property": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_authentication": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "user_view_type": { + "type": "string" + } + }, + "type": "object" + }, + "public": { + "type": "boolean" + }, + "updated_at": { + "format": "date-time", + "type": "string" + } + }, + "type": "object" + } +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/get_repository_tree.snap b/pkg/github/__toolsnaps__/get_repository_tree.snap index 6ba368f2f2..bf7b24bb4f 100644 --- a/pkg/github/__toolsnaps__/get_repository_tree.snap +++ b/pkg/github/__toolsnaps__/get_repository_tree.snap @@ -35,5 +35,79 @@ ], "type": "object" }, - "name": "get_repository_tree" + "name": "get_repository_tree", + "outputSchema": { + "additionalProperties": false, + "properties": { + "count": { + "type": "integer" + }, + "owner": { + "type": "string" + }, + "recursive": { + "type": "boolean" + }, + "repo": { + "type": "string" + }, + "sha": { + "type": "string" + }, + "tree": { + "items": { + "additionalProperties": false, + "properties": { + "mode": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "path", + "type", + "mode", + "sha", + "url" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "tree_sha": { + "type": "string" + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "sha", + "truncated", + "tree", + "tree_sha", + "owner", + "repo", + "recursive", + "count" + ], + "type": "object" + } } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/get_repository_tree_typed.snap b/pkg/github/__toolsnaps__/get_repository_tree_typed.snap new file mode 100644 index 0000000000..c1e2d076c2 --- /dev/null +++ b/pkg/github/__toolsnaps__/get_repository_tree_typed.snap @@ -0,0 +1,127 @@ +{ + "annotations": { + "idempotentHint": false, + "readOnlyHint": true, + "title": "Get repository tree" + }, + "description": "Get the tree structure (files and directories) of a GitHub repository at a specific ref or SHA", + "icons": [ + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAACjUlEQVRIicWVTUhUURTHf+e+5weEVsokswnFJ+O8cCYMCkxrUYsioW1E7dyF0E4IDHXRok1BRYsiiGrTJtAIEqIiySDTwdBRGLVd6sig0MKPmXda2OiQ88Sv6KzOO+d/z++ee967D/6xyd+B6tq6Y0akEWW20Mr0jIyM/NqsQCgUKvGkqBlDQJW+RHx40BfguNFOlPac+JTl6Znx8eGp/MUjVRkj74CqPyFFpCsxGuvIakzuzlHaRXjkLdkHVOUkaGnGcMdv9xkxd0FLUdOQLpKDoI9RvemEI/UbACJWEyArhdI2OfltYWIs9lkxz0FO+56P6CngWWJsqP9HLDa/bGsbIAJNGwHqzQBYi7jrFbwjCjN+9VdzsqYvWLZcAC9njZ11Cq1Mz5JnT4l43dW10ReIuoKcBbnm2wByD/S+E472qhIX0SsIk8WSeZ3VWFknmUwuB8orulVwDXJJxRQLciMRjz30A6TmpgfKAsEkygUjNALvbZXL8fj3Wb81ADjhqDru0Y5NRbl6N9rphKNevpzJF9y2KRHgZ76UnS+4VXOcE6XYiy3ARVQ7twVQ5bgTjo4DNeT54ldtMeu83L+v4Na2AIKeR3iL8spPg+oCmP7EWOyDn8T/iEQmEqOxc775LZrvkBV5s9vimwIETe0FwMp9CIUiVQcDwacClYrWlAeCK6m56YE9AYRCoZK0Kfgioo6KeSKrb05rWSCYTM1Nf90pYG3InhQ1C1SiVsPE2FA/gBOO9iraCjzYKWB9BsIhgHSxxrMhRUYFKnZaHHI6UPgEqL3k3T5cV9dWsGy5gl4F/bgbwFoHifjwICJdIC2FaZMSo30I87aa67sBbLgCnHCkXoRGPJJb+en/d/sN+mTh5OWORQYAAAAASUVORK5CYII=", + "theme": "light" + }, + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABkUlEQVRIicWVvU4CQRSFz5DYiWKhttJiYgEV6iOYWPsKUmh8AnkCsZQ3sFJjYgI2FiZ2xsqfRDRWxl9itLDis/CKI+5OQCCeZnbPnnvPnZ25M1Kf4VoJICdpVtK9pF3n3FsoAZCUNCdpVNKhc+44JC4CDb5xBaQD+rRpvtAAVuPEORNsAMPANPAIbAcMdkyTB1JA2XJko8TLVkXK40pAPWBQB9a89xHLsfTFJTz9nY0Zj5v0+CjctegzHv+rmkH7n4/AOrBv1SwGZlAwTdVinoAaMBgXkAa2gXfgBlgEfu00T+/MpAa8AlvARGDGzUBid0O0vgg0or4losg/YErSbc8NgCFgRdK8pHIngQB7wEVL48VhExiIyhW5gAD2WJF0EqjlRdKRc+6g7eq9GVx2FBSD0Brs9dvguRcGP2CNtuM1WiHUaJ0mTwLXdlSUgIqtRaFXBguWMO9xVeC8m7z+GozZeOZxp5LGuzFoAshaU5XtXJ+x0zH2wvmLyWpL59baOh0DiLr0s/q89B/UxqX/7/gAj6/Ekn4d+MEAAAAASUVORK5CYII=", + "theme": "dark" + } + ], + "inputSchema": { + "properties": { + "owner": { + "description": "Repository owner (username or organization)", + "type": "string", + "x-mcp-header": "owner" + }, + "path_filter": { + "description": "Optional path prefix to filter the tree results (e.g., 'src/' to only show files in the src directory)", + "type": "string" + }, + "recursive": { + "default": false, + "description": "Setting this parameter to true returns the objects or subtrees referenced by the tree. Default is false", + "type": "boolean" + }, + "repo": { + "description": "Repository name", + "type": "string", + "x-mcp-header": "repo" + }, + "tree_sha": { + "description": "The SHA1 value or ref (branch or tag) name of the tree. Defaults to the repository's default branch", + "type": "string" + } + }, + "required": [ + "owner", + "repo" + ], + "type": "object" + }, + "name": "get_repository_tree", + "outputSchema": { + "additionalProperties": false, + "properties": { + "count": { + "type": "integer" + }, + "owner": { + "type": "string" + }, + "recursive": { + "type": "boolean" + }, + "repo": { + "type": "string" + }, + "sha": { + "type": "string" + }, + "tree": { + "items": { + "additionalProperties": false, + "properties": { + "mode": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "path", + "type", + "mode", + "sha", + "url" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "tree_sha": { + "type": "string" + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "sha", + "truncated", + "tree", + "tree_sha", + "owner", + "repo", + "recursive", + "count" + ], + "type": "object" + } +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/list_gists.snap b/pkg/github/__toolsnaps__/list_gists.snap index 8f09ff3880..eb90f2f75e 100644 --- a/pkg/github/__toolsnaps__/list_gists.snap +++ b/pkg/github/__toolsnaps__/list_gists.snap @@ -29,5 +29,641 @@ }, "type": "object" }, - "name": "list_gists" + "name": "list_gists", + "outputSchema": { + "$defs": { + "team": { + "additionalProperties": false, + "properties": { + "access_source": { + "type": "string" + }, + "assignment": { + "type": "string" + }, + "description": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ldap_dn": { + "type": "string" + }, + "members_count": { + "type": "integer" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_setting": { + "type": "string" + }, + "organization": { + "additionalProperties": false, + "properties": { + "advanced_security_enabled_for_new_repositories": { + "type": "boolean" + }, + "archived_at": { + "format": "date-time", + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "billing_email": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "default_repository_branch": { + "type": "string" + }, + "default_repository_permission": { + "type": "string" + }, + "default_repository_settings": { + "type": "string" + }, + "dependabot_alerts_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependabot_security_updates_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependency_graph_enabled_for_new_repositories": { + "type": "boolean" + }, + "deploy_keys_enabled_for_repositories": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "display_commenter_full_name_setting_enabled": { + "type": "boolean" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "following": { + "type": "integer" + }, + "has_organization_projects": { + "type": "boolean" + }, + "has_repository_projects": { + "type": "boolean" + }, + "hooks_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_verified": { + "type": "boolean" + }, + "issues_url": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "members_allowed_repository_creation_type": { + "type": "string" + }, + "members_can_change_repo_visibility": { + "type": "boolean" + }, + "members_can_create_internal_repositories": { + "type": "boolean" + }, + "members_can_create_pages": { + "type": "boolean" + }, + "members_can_create_private_pages": { + "type": "boolean" + }, + "members_can_create_private_repositories": { + "type": "boolean" + }, + "members_can_create_public_pages": { + "type": "boolean" + }, + "members_can_create_public_repositories": { + "type": "boolean" + }, + "members_can_create_repositories": { + "type": "boolean" + }, + "members_can_create_teams": { + "type": "boolean" + }, + "members_can_delete_issues": { + "type": "boolean" + }, + "members_can_delete_repositories": { + "type": "boolean" + }, + "members_can_fork_private_repositories": { + "type": "boolean" + }, + "members_can_invite_outside_collaborators": { + "type": "boolean" + }, + "members_can_view_dependency_insights": { + "type": "boolean" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_members_url": { + "type": "string" + }, + "public_repos": { + "type": "integer" + }, + "readers_can_create_discussions": { + "type": "boolean" + }, + "repos_url": { + "type": "string" + }, + "secret_scanning_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_push_protection_custom_link": { + "type": "string" + }, + "secret_scanning_push_protection_custom_link_enabled": { + "type": "boolean" + }, + "secret_scanning_push_protection_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_validity_checks_enabled": { + "type": "boolean" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_requirement_enabled": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "web_commit_signoff_required": { + "type": "boolean" + } + }, + "type": "object" + }, + "parent": { + "$ref": "#/$defs/team", + "type": "object" + }, + "permission": { + "type": "string" + }, + "permissions": { + "additionalProperties": { + "type": "boolean" + }, + "type": "object" + }, + "privacy": { + "type": "string" + }, + "repos_count": { + "type": "integer" + }, + "repositories_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "object", + "null" + ] + } + }, + "items": { + "additionalProperties": false, + "properties": { + "comments": { + "type": "integer" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "description": { + "type": "string" + }, + "files": { + "additionalProperties": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "language": { + "type": "string" + }, + "raw_url": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "type": "object" + }, + "git_pull_url": { + "type": "string" + }, + "git_push_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owner": { + "additionalProperties": false, + "properties": { + "assignment": { + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "business_plus": { + "type": "boolean" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "followers_url": { + "type": "string" + }, + "following": { + "type": "integer" + }, + "following_url": { + "type": "string" + }, + "gists_url": { + "type": "string" + }, + "gravatar_id": { + "type": "string" + }, + "hireable": { + "type": "boolean" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "inherited": { + "type": "boolean" + }, + "inherited_from": { + "items": { + "$ref": "#/$defs/team", + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "ldap_dn": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_email": { + "type": "string" + }, + "organizations_url": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "permissions": { + "additionalProperties": false, + "properties": { + "admin": { + "type": "boolean" + }, + "maintain": { + "type": "boolean" + }, + "pull": { + "type": "boolean" + }, + "push": { + "type": "boolean" + }, + "triage": { + "type": "boolean" + } + }, + "type": "object" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_repos": { + "type": "integer" + }, + "received_events_url": { + "type": "string" + }, + "repos_url": { + "type": "string" + }, + "role": { + "type": "string" + }, + "role_name": { + "type": "string" + }, + "site_admin": { + "type": "boolean" + }, + "starred_url": { + "type": "string" + }, + "subscriptions_url": { + "type": "string" + }, + "suspended_at": { + "format": "date-time", + "type": "string" + }, + "text_matches": { + "items": { + "additionalProperties": false, + "properties": { + "fragment": { + "type": "string" + }, + "matches": { + "items": { + "additionalProperties": false, + "properties": { + "indices": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "text": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "object_type": { + "type": "string" + }, + "object_url": { + "type": "string" + }, + "property": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_authentication": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "user_view_type": { + "type": "string" + } + }, + "type": "object" + }, + "public": { + "type": "boolean" + }, + "updated_at": { + "format": "date-time", + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": [ + "null", + "array" + ] + } } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/list_gists_typed.snap b/pkg/github/__toolsnaps__/list_gists_typed.snap new file mode 100644 index 0000000000..8bfa077040 --- /dev/null +++ b/pkg/github/__toolsnaps__/list_gists_typed.snap @@ -0,0 +1,681 @@ +{ + "annotations": { + "idempotentHint": false, + "readOnlyHint": true, + "title": "List Gists" + }, + "description": "List gists for a user", + "icons": [ + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAAD2ElEQVRIie2WS2xUVRjHf9/tHSgjRCA+C5am3gFmhnIHJypEY4aVujAYfCUGF8SFRhONgiRsiInxFRIiGhcSxQVEhYhhRQyycuMjUWaG0mnLLQ9BUORRiK20nTl/F1DSDJe2VOLC+F/dc873/c/vfDfnAf/rPyIb2Qgy4SI5njNjKTAb8BG9GBHSbmfeZwcrxQP/KlgqnVsj9CZQM9jlYL+JIYxbgEXAYrBDUaUYABQKBf/Yb2dXy6yzp1Lceb3BfIAgE66Q9C7wcw1v+aHK3iP1gXPnhrOcr6XD7eO/nytgvG2oD5g6kclbs+H9npgbdZQ2XwHWlM8n6a+uB51ucA2PRF17j8eZdHeXfgW2Drfd0KQflRj43OT2TQQKwHN8KjgBXAl2Q9/QMpndZti6rqtAxSmKfjgPPD1RqLHkyexhAM9p+/X2bm5rm9HS0tI4kWQL0uFeYE5UKc281uQgHR6W+Lqns/T85b5MuAjHWxgFYBjqDLCnP+mvnHHhQuNALbEK00Nc3FQDGCP/1Nqoo7TdB5qBQxNZFTDdjORwozWdSyF9i8dJnN4R3nHPc1PkvFsxd58/ODh5aKjRk1c7guwjQ29gnJez9cMeDa5Whou7cipwPm7WVCZc6ZwlRvb5ct90dZVjF+IZKxG+fC3pKZdPjrKgTQBBOnxNcKKns7ipPsAHDENx2RIbzTRtZF+tgSe4WoWlOxCnxoAalzygz1z8OVSdbM2Dvps56LuZJh4b081UwZgVpHMvNOXzyTHjR5EPHJXREjd4uFjsHf4O5od/jmWW9NnYP6R7gA+T/dUP7kyHvxi0Y/aTvOonPe3tR8cL5gEdwM3z5+di4a5F5XK5L6qUH63htZj0JLAFVENaa7WGfakFd6XH6+Vj7EY8VUXLgQ3/FA7g0pV2BNgBEKQXLgb7Tq72LLB6PB5edZJ9BZzFeLUll5t+PcDqFVXK3wMXJGuqHzPDiwU7XCz2mul1YFZiQF9ms9lrOGhVpe7pFKdUuu0BoNGM7rqhMxKt+Xw+UZ/jAxzoKL8fZMI5Eq8MOL8rSIfbwDokqoamGna7TA/WJ5vsoIx7U9lc7sD+YjGVWfiSZHcbdGP84SDhwQKJFcApBt3HdRZfGGw491d1R5AJt+LojTpLuy+DAUQdpVWtmYW7GmQvClaAbrRLtXDohGFFYHM1YXsu1wtbg7RFTquAZ5DXI/Q4sAwxzQDBWUw78dy6qNJ+bCRVVCm9F6RzkvSywTZMp4GbRi1/Uz6fbG5rm1EoFPxRA4G4i7opn0/OmzdvWlx8nGbPXjIlm81O6F33vwD+Bhvyhr7wtSBQAAAAAElFTkSuQmCC", + "theme": "light" + }, + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAACoElEQVRIie2WX2hXZRjHP8/4hW5UzoGFja2wdiF00S7EotAgIcKbMBoIu7HuuhANC3aRCTIQgmBBsEZ3s8hoIwomrLuB4E0YhqWFrjHY0NY2rGHh6uPFecXj2flt+/2mXoRfOHDO+z5/Ps973vO8B+7r/yi1U+1XL6gL6j/qZfWU+r7aca9YIgf1LtAL/AuMAOeA68AjQCfwLDAeEU8l+wpwCDgfEV/fFTq120zfq49XsWlVu3PPu5LPX2vI+4L6RrXJJnVanVEfqyHow+rnas8awH5Vx6pN7k2Vv1dvgnq1HFgD8Eq6//IOJ21QN6rr6w1wRp2t0/c3tb8w1qmeVK95S3+oJ9K2aVF7037+L9ldzF1dABWgHRivqypoBppyUB3AGHAFOAZMAY3Ao8DzwDqytzQBfAIcBa4CH+Rinr0J9mCaLFuRfcADheHvIqJaIftSzOci4soyBQ2k+O8A0xExUDSokPUyqwToAx4qjL1O9RVuA2ZWgFqVGoAFslUrUzvQkq7XVhHvZ6BVfUttWtF6BbBJ4ImyyYiYj4i5iJgDVtNI+4BvgI+BP9Vx9Vv1iNpWK9hPwCa1FK4WRcRCRLxKVmgXMEh2xPUAP6pbawEbTfd71gqWA5yIiKGIOJxAdwIbgDdrARsG5oC31eY7BVcAPQ38DZQdeQ2lYBExDxwBWoGv1JYaci6S+0OpJnUHsB74pTA1C2xRiy2JCkBEfJT+Kg4CF9QTZHtvkeyL3Qy8XJLzErBdfSYiflD3A9sSwO9kPfBpoBuYAT4t+H8BfAgMqceB+YgYpSj1JXVYnfd2Takj6oH861ZfVCfVwfS8Wx1Tr+Z8Z9XP1CdL8kWKOZ5sZ0qKX+LUlA7hyipslxzUyb/YnJeL0ahW66f3taJuAAWd129KkzycAAAAAElFTkSuQmCC", + "theme": "dark" + } + ], + "inputSchema": { + "properties": { + "page": { + "description": "Page number for pagination (min 1)", + "minimum": 1, + "type": "number" + }, + "perPage": { + "description": "Results per page for pagination (min 1, max 100)", + "maximum": 100, + "minimum": 1, + "type": "number" + }, + "since": { + "description": "Only gists updated after this time (ISO 8601 timestamp)", + "type": "string" + }, + "username": { + "description": "GitHub username (omit for authenticated user's gists)", + "type": "string" + } + }, + "type": "object" + }, + "name": "list_gists", + "outputSchema": { + "$defs": { + "team": { + "additionalProperties": false, + "properties": { + "access_source": { + "type": "string" + }, + "assignment": { + "type": "string" + }, + "description": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "ldap_dn": { + "type": "string" + }, + "members_count": { + "type": "integer" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_setting": { + "type": "string" + }, + "organization": { + "additionalProperties": false, + "properties": { + "advanced_security_enabled_for_new_repositories": { + "type": "boolean" + }, + "archived_at": { + "format": "date-time", + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "billing_email": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "default_repository_branch": { + "type": "string" + }, + "default_repository_permission": { + "type": "string" + }, + "default_repository_settings": { + "type": "string" + }, + "dependabot_alerts_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependabot_security_updates_enabled_for_new_repositories": { + "type": "boolean" + }, + "dependency_graph_enabled_for_new_repositories": { + "type": "boolean" + }, + "deploy_keys_enabled_for_repositories": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "display_commenter_full_name_setting_enabled": { + "type": "boolean" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "following": { + "type": "integer" + }, + "has_organization_projects": { + "type": "boolean" + }, + "has_repository_projects": { + "type": "boolean" + }, + "hooks_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "is_verified": { + "type": "boolean" + }, + "issues_url": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "members_allowed_repository_creation_type": { + "type": "string" + }, + "members_can_change_repo_visibility": { + "type": "boolean" + }, + "members_can_create_internal_repositories": { + "type": "boolean" + }, + "members_can_create_pages": { + "type": "boolean" + }, + "members_can_create_private_pages": { + "type": "boolean" + }, + "members_can_create_private_repositories": { + "type": "boolean" + }, + "members_can_create_public_pages": { + "type": "boolean" + }, + "members_can_create_public_repositories": { + "type": "boolean" + }, + "members_can_create_repositories": { + "type": "boolean" + }, + "members_can_create_teams": { + "type": "boolean" + }, + "members_can_delete_issues": { + "type": "boolean" + }, + "members_can_delete_repositories": { + "type": "boolean" + }, + "members_can_fork_private_repositories": { + "type": "boolean" + }, + "members_can_invite_outside_collaborators": { + "type": "boolean" + }, + "members_can_view_dependency_insights": { + "type": "boolean" + }, + "members_url": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_members_url": { + "type": "string" + }, + "public_repos": { + "type": "integer" + }, + "readers_can_create_discussions": { + "type": "boolean" + }, + "repos_url": { + "type": "string" + }, + "secret_scanning_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_push_protection_custom_link": { + "type": "string" + }, + "secret_scanning_push_protection_custom_link_enabled": { + "type": "boolean" + }, + "secret_scanning_push_protection_enabled_for_new_repositories": { + "type": "boolean" + }, + "secret_scanning_validity_checks_enabled": { + "type": "boolean" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_requirement_enabled": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "web_commit_signoff_required": { + "type": "boolean" + } + }, + "type": "object" + }, + "parent": { + "$ref": "#/$defs/team", + "type": "object" + }, + "permission": { + "type": "string" + }, + "permissions": { + "additionalProperties": { + "type": "boolean" + }, + "type": "object" + }, + "privacy": { + "type": "string" + }, + "repos_count": { + "type": "integer" + }, + "repositories_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": [ + "object", + "null" + ] + } + }, + "items": { + "additionalProperties": false, + "properties": { + "comments": { + "type": "integer" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "description": { + "type": "string" + }, + "files": { + "additionalProperties": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "language": { + "type": "string" + }, + "raw_url": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "type": "object" + }, + "git_pull_url": { + "type": "string" + }, + "git_push_url": { + "type": "string" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "owner": { + "additionalProperties": false, + "properties": { + "assignment": { + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "blog": { + "type": "string" + }, + "business_plus": { + "type": "boolean" + }, + "collaborators": { + "type": "integer" + }, + "company": { + "type": "string" + }, + "created_at": { + "format": "date-time", + "type": "string" + }, + "disk_usage": { + "type": "integer" + }, + "email": { + "type": "string" + }, + "events_url": { + "type": "string" + }, + "followers": { + "type": "integer" + }, + "followers_url": { + "type": "string" + }, + "following": { + "type": "integer" + }, + "following_url": { + "type": "string" + }, + "gists_url": { + "type": "string" + }, + "gravatar_id": { + "type": "string" + }, + "hireable": { + "type": "boolean" + }, + "html_url": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "inherited": { + "type": "boolean" + }, + "inherited_from": { + "items": { + "$ref": "#/$defs/team", + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "ldap_dn": { + "type": "string" + }, + "location": { + "type": "string" + }, + "login": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node_id": { + "type": "string" + }, + "notification_email": { + "type": "string" + }, + "organizations_url": { + "type": "string" + }, + "owned_private_repos": { + "type": "integer" + }, + "permissions": { + "additionalProperties": false, + "properties": { + "admin": { + "type": "boolean" + }, + "maintain": { + "type": "boolean" + }, + "pull": { + "type": "boolean" + }, + "push": { + "type": "boolean" + }, + "triage": { + "type": "boolean" + } + }, + "type": "object" + }, + "plan": { + "additionalProperties": false, + "properties": { + "collaborators": { + "type": "integer" + }, + "filled_seats": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "private_repos": { + "type": "integer" + }, + "seats": { + "type": "integer" + }, + "space": { + "type": "integer" + } + }, + "type": "object" + }, + "private_gists": { + "type": "integer" + }, + "public_gists": { + "type": "integer" + }, + "public_repos": { + "type": "integer" + }, + "received_events_url": { + "type": "string" + }, + "repos_url": { + "type": "string" + }, + "role": { + "type": "string" + }, + "role_name": { + "type": "string" + }, + "site_admin": { + "type": "boolean" + }, + "starred_url": { + "type": "string" + }, + "subscriptions_url": { + "type": "string" + }, + "suspended_at": { + "format": "date-time", + "type": "string" + }, + "text_matches": { + "items": { + "additionalProperties": false, + "properties": { + "fragment": { + "type": "string" + }, + "matches": { + "items": { + "additionalProperties": false, + "properties": { + "indices": { + "items": { + "type": "integer" + }, + "type": "array" + }, + "text": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "object_type": { + "type": "string" + }, + "object_url": { + "type": "string" + }, + "property": { + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": "array" + }, + "total_private_repos": { + "type": "integer" + }, + "twitter_username": { + "type": "string" + }, + "two_factor_authentication": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "updated_at": { + "format": "date-time", + "type": "string" + }, + "url": { + "type": "string" + }, + "user_view_type": { + "type": "string" + } + }, + "type": "object" + }, + "public": { + "type": "boolean" + }, + "updated_at": { + "format": "date-time", + "type": "string" + } + }, + "type": [ + "null", + "object" + ] + }, + "type": [ + "null", + "array" + ] + } +} \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/update_gist.snap b/pkg/github/__toolsnaps__/update_gist.snap index c004a8bf14..ec3ba59278 100644 --- a/pkg/github/__toolsnaps__/update_gist.snap +++ b/pkg/github/__toolsnaps__/update_gist.snap @@ -31,5 +31,21 @@ ], "type": "object" }, - "name": "update_gist" + "name": "update_gist", + "outputSchema": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "url" + ], + "type": "object" + } } \ No newline at end of file diff --git a/pkg/github/__toolsnaps__/update_gist_typed.snap b/pkg/github/__toolsnaps__/update_gist_typed.snap new file mode 100644 index 0000000000..3f4f8820a0 --- /dev/null +++ b/pkg/github/__toolsnaps__/update_gist_typed.snap @@ -0,0 +1,63 @@ +{ + "annotations": { + "idempotentHint": false, + "readOnlyHint": false, + "title": "Update Gist" + }, + "description": "Update an existing gist", + "icons": [ + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAAD2ElEQVRIie2WS2xUVRjHf9/tHSgjRCA+C5am3gFmhnIHJypEY4aVujAYfCUGF8SFRhONgiRsiInxFRIiGhcSxQVEhYhhRQyycuMjUWaG0mnLLQ9BUORRiK20nTl/F1DSDJe2VOLC+F/dc873/c/vfDfnAf/rPyIb2Qgy4SI5njNjKTAb8BG9GBHSbmfeZwcrxQP/KlgqnVsj9CZQM9jlYL+JIYxbgEXAYrBDUaUYABQKBf/Yb2dXy6yzp1Lceb3BfIAgE66Q9C7wcw1v+aHK3iP1gXPnhrOcr6XD7eO/nytgvG2oD5g6kclbs+H9npgbdZQ2XwHWlM8n6a+uB51ucA2PRF17j8eZdHeXfgW2Drfd0KQflRj43OT2TQQKwHN8KjgBXAl2Q9/QMpndZti6rqtAxSmKfjgPPD1RqLHkyexhAM9p+/X2bm5rm9HS0tI4kWQL0uFeYE5UKc281uQgHR6W+Lqns/T85b5MuAjHWxgFYBjqDLCnP+mvnHHhQuNALbEK00Nc3FQDGCP/1Nqoo7TdB5qBQxNZFTDdjORwozWdSyF9i8dJnN4R3nHPc1PkvFsxd58/ODh5aKjRk1c7guwjQ29gnJez9cMeDa5Whou7cipwPm7WVCZc6ZwlRvb5ct90dZVjF+IZKxG+fC3pKZdPjrKgTQBBOnxNcKKns7ipPsAHDENx2RIbzTRtZF+tgSe4WoWlOxCnxoAalzygz1z8OVSdbM2Dvps56LuZJh4b081UwZgVpHMvNOXzyTHjR5EPHJXREjd4uFjsHf4O5od/jmWW9NnYP6R7gA+T/dUP7kyHvxi0Y/aTvOonPe3tR8cL5gEdwM3z5+di4a5F5XK5L6qUH63htZj0JLAFVENaa7WGfakFd6XH6+Vj7EY8VUXLgQ3/FA7g0pV2BNgBEKQXLgb7Tq72LLB6PB5edZJ9BZzFeLUll5t+PcDqFVXK3wMXJGuqHzPDiwU7XCz2mul1YFZiQF9ms9lrOGhVpe7pFKdUuu0BoNGM7rqhMxKt+Xw+UZ/jAxzoKL8fZMI5Eq8MOL8rSIfbwDokqoamGna7TA/WJ5vsoIx7U9lc7sD+YjGVWfiSZHcbdGP84SDhwQKJFcApBt3HdRZfGGw491d1R5AJt+LojTpLuy+DAUQdpVWtmYW7GmQvClaAbrRLtXDohGFFYHM1YXsu1wtbg7RFTquAZ5DXI/Q4sAwxzQDBWUw78dy6qNJ+bCRVVCm9F6RzkvSywTZMp4GbRi1/Uz6fbG5rm1EoFPxRA4G4i7opn0/OmzdvWlx8nGbPXjIlm81O6F33vwD+Bhvyhr7wtSBQAAAAAElFTkSuQmCC", + "theme": "light" + }, + { + "mimeType": "image/png", + "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAYCAYAAACWTY9zAAAABmJLR0QA/wD/AP+gvaeTAAACoElEQVRIie2WX2hXZRjHP8/4hW5UzoGFja2wdiF00S7EotAgIcKbMBoIu7HuuhANC3aRCTIQgmBBsEZ3s8hoIwomrLuB4E0YhqWFrjHY0NY2rGHh6uPFecXj2flt+/2mXoRfOHDO+z5/Ps973vO8B+7r/yi1U+1XL6gL6j/qZfWU+r7aca9YIgf1LtAL/AuMAOeA68AjQCfwLDAeEU8l+wpwCDgfEV/fFTq120zfq49XsWlVu3PPu5LPX2vI+4L6RrXJJnVanVEfqyHow+rnas8awH5Vx6pN7k2Vv1dvgnq1HFgD8Eq6//IOJ21QN6rr6w1wRp2t0/c3tb8w1qmeVK95S3+oJ9K2aVF7037+L9ldzF1dABWgHRivqypoBppyUB3AGHAFOAZMAY3Ao8DzwDqytzQBfAIcBa4CH+Rinr0J9mCaLFuRfcADheHvIqJaIftSzOci4soyBQ2k+O8A0xExUDSokPUyqwToAx4qjL1O9RVuA2ZWgFqVGoAFslUrUzvQkq7XVhHvZ6BVfUttWtF6BbBJ4ImyyYiYj4i5iJgDVtNI+4BvgI+BP9Vx9Vv1iNpWK9hPwCa1FK4WRcRCRLxKVmgXMEh2xPUAP6pbawEbTfd71gqWA5yIiKGIOJxAdwIbgDdrARsG5oC31eY7BVcAPQ38DZQdeQ2lYBExDxwBWoGv1JYaci6S+0OpJnUHsB74pTA1C2xRiy2JCkBEfJT+Kg4CF9QTZHtvkeyL3Qy8XJLzErBdfSYiflD3A9sSwO9kPfBpoBuYAT4t+H8BfAgMqceB+YgYpSj1JXVYnfd2Takj6oH861ZfVCfVwfS8Wx1Tr+Z8Z9XP1CdL8kWKOZ5sZ0qKX+LUlA7hyipslxzUyb/YnJeL0ahW66f3taJuAAWd129KkzycAAAAAElFTkSuQmCC", + "theme": "dark" + } + ], + "inputSchema": { + "properties": { + "content": { + "description": "Content for the file", + "type": "string" + }, + "description": { + "description": "Updated description of the gist", + "type": "string" + }, + "filename": { + "description": "Filename to update or create", + "type": "string" + }, + "gist_id": { + "description": "ID of the gist to update", + "type": "string" + } + }, + "required": [ + "gist_id", + "filename", + "content" + ], + "type": "object" + }, + "name": "update_gist", + "outputSchema": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "url" + ], + "type": "object" + } +} \ No newline at end of file diff --git a/pkg/github/gists.go b/pkg/github/gists.go index 44baba5c6d..2f4d4fd2cb 100644 --- a/pkg/github/gists.go +++ b/pkg/github/gists.go @@ -23,8 +23,9 @@ func ListGists(t translations.TranslationHelperFunc) inventory.ServerTool { return NewTool( ToolsetMetadataGists, mcp.Tool{ - Name: "list_gists", - Description: t("TOOL_LIST_GISTS_DESCRIPTION", "List gists for a user"), + Name: "list_gists", + OutputSchema: gistListOutputSchema(), + Description: t("TOOL_LIST_GISTS_DESCRIPTION", "List gists for a user"), Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_LIST_GISTS", "List Gists"), ReadOnlyHint: true, @@ -44,7 +45,7 @@ func ListGists(t translations.TranslationHelperFunc) inventory.ServerTool { }), }, scopes.NoScopes(), - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, []*github.Gist, error) { username, err := OptionalParam[string](args, "username") if err != nil { return utils.NewToolResultError(err.Error()), nil, nil @@ -102,8 +103,9 @@ func ListGists(t translations.TranslationHelperFunc) inventory.ServerTool { result := utils.NewToolResultText(string(r)) result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelGistList()) - return result, nil, nil + return result, gists, nil }, + gitGistNormalizer(validateListGistsArguments), ) } @@ -112,8 +114,9 @@ func GetGist(t translations.TranslationHelperFunc) inventory.ServerTool { return NewTool( ToolsetMetadataGists, mcp.Tool{ - Name: "get_gist", - Description: t("TOOL_GET_GIST_DESCRIPTION", "Get gist content of a particular gist, by gist ID"), + Name: "get_gist", + OutputSchema: gistOutputSchema(), + Description: t("TOOL_GET_GIST_DESCRIPTION", "Get gist content of a particular gist, by gist ID"), Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_GET_GIST", "Get Gist Content"), ReadOnlyHint: true, @@ -130,7 +133,7 @@ func GetGist(t translations.TranslationHelperFunc) inventory.ServerTool { }, }, scopes.NoScopes(), - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, *github.Gist, error) { gistID, err := RequiredParam[string](args, "gist_id") if err != nil { return utils.NewToolResultError(err.Error()), nil, nil @@ -162,8 +165,9 @@ func GetGist(t translations.TranslationHelperFunc) inventory.ServerTool { result := utils.NewToolResultText(string(r)) result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelGist()) - return result, nil, nil + return result, gist, nil }, + gitGistNormalizer(validateGetGistArguments), ) } @@ -172,8 +176,9 @@ func CreateGist(t translations.TranslationHelperFunc) inventory.ServerTool { return NewTool( ToolsetMetadataGists, mcp.Tool{ - Name: "create_gist", - Description: t("TOOL_CREATE_GIST_DESCRIPTION", "Create a new gist"), + Name: "create_gist", + OutputSchema: gistMutationOutputSchema(), + Description: t("TOOL_CREATE_GIST_DESCRIPTION", "Create a new gist"), Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_CREATE_GIST", "Create Gist"), ReadOnlyHint: false, @@ -203,7 +208,7 @@ func CreateGist(t translations.TranslationHelperFunc) inventory.ServerTool { }, }, scopes.RequireAll(scopes.Gist), - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, *MinimalResponse, error) { description, err := OptionalParam[string](args, "description") if err != nil { return utils.NewToolResultError(err.Error()), nil, nil @@ -264,8 +269,9 @@ func CreateGist(t translations.TranslationHelperFunc) inventory.ServerTool { return utils.NewToolResultErrorFromErr("failed to marshal response", err), nil, nil } - return utils.NewToolResultText(string(r)), nil, nil + return utils.NewToolResultText(string(r)), &minimalResponse, nil }, + gitGistNormalizer(validateCreateGistArguments), ) } @@ -274,8 +280,9 @@ func UpdateGist(t translations.TranslationHelperFunc) inventory.ServerTool { return NewTool( ToolsetMetadataGists, mcp.Tool{ - Name: "update_gist", - Description: t("TOOL_UPDATE_GIST_DESCRIPTION", "Update an existing gist"), + Name: "update_gist", + OutputSchema: gistMutationOutputSchema(), + Description: t("TOOL_UPDATE_GIST_DESCRIPTION", "Update an existing gist"), Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_UPDATE_GIST", "Update Gist"), ReadOnlyHint: false, @@ -304,7 +311,7 @@ func UpdateGist(t translations.TranslationHelperFunc) inventory.ServerTool { }, }, scopes.RequireAll(scopes.Gist), - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, *MinimalResponse, error) { gistID, err := RequiredParam[string](args, "gist_id") if err != nil { return utils.NewToolResultError(err.Error()), nil, nil @@ -373,7 +380,8 @@ func UpdateGist(t translations.TranslationHelperFunc) inventory.ServerTool { return utils.NewToolResultErrorFromErr("failed to marshal response", err), nil, nil } - return utils.NewToolResultText(string(r)), nil, nil + return utils.NewToolResultText(string(r)), &minimalResponse, nil }, + gitGistNormalizer(validateUpdateGistArguments), ) } diff --git a/pkg/github/git.go b/pkg/github/git.go index b066dfe757..e9df02b514 100644 --- a/pkg/github/git.go +++ b/pkg/github/git.go @@ -44,8 +44,9 @@ func GetRepositoryTree(t translations.TranslationHelperFunc) inventory.ServerToo return NewTool( ToolsetMetadataGit, mcp.Tool{ - Name: "get_repository_tree", - Description: t("TOOL_GET_REPOSITORY_TREE_DESCRIPTION", "Get the tree structure (files and directories) of a GitHub repository at a specific ref or SHA"), + Name: "get_repository_tree", + OutputSchema: treeOutputSchema(), + Description: t("TOOL_GET_REPOSITORY_TREE_DESCRIPTION", "Get the tree structure (files and directories) of a GitHub repository at a specific ref or SHA"), Annotations: &mcp.ToolAnnotations{ Title: t("TOOL_GET_REPOSITORY_TREE_USER_TITLE", "Get repository tree"), ReadOnlyHint: true, @@ -79,7 +80,7 @@ func GetRepositoryTree(t translations.TranslationHelperFunc) inventory.ServerToo }, }, scopes.PublicRead(scopes.Repo), - func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, any, error) { + func(ctx context.Context, deps ToolDependencies, _ *mcp.CallToolRequest, args map[string]any) (*mcp.CallToolResult, *TreeResponse, error) { owner, err := RequiredParam[string](args, "owner") if err != nil { return utils.NewToolResultError(err.Error()), nil, nil @@ -178,7 +179,8 @@ func GetRepositoryTree(t translations.TranslationHelperFunc) inventory.ServerToo // repos only collaborators can (trusted). Confidentiality follows // repo visibility. result = attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, result, ifc.LabelCommitContents) - return result, nil, nil + return result, &response, nil }, + gitGistNormalizer(validateTreeArguments), ) } diff --git a/pkg/github/git_gist_types.go b/pkg/github/git_gist_types.go new file mode 100644 index 0000000000..ee378397e4 --- /dev/null +++ b/pkg/github/git_gist_types.go @@ -0,0 +1,142 @@ +package github + +import ( + "encoding/json" + "slices" + + "github.com/github/github-mcp-server/pkg/inventory" + "github.com/google/go-github/v92/github" + "github.com/google/jsonschema-go/jsonschema" +) + +// gitGistNormalizer applies the legacy argument validation, in legacy order and +// with legacy messages, before SDK schema validation can reject the request. +// The optional canonicalize hook rewrites accepted legacy values (such as +// numeric strings) into schema-valid ones. +func gitGistNormalizer(validate func(args map[string]any) error) inventory.InputNormalizer { + return func(raw json.RawMessage) (json.RawMessage, error) { + var args map[string]any + if err := json.Unmarshal(raw, &args); err != nil { + return nil, &inventory.ToolInputError{Message: "invalid arguments: " + err.Error()} + } + if args == nil { + return nil, &inventory.ToolInputError{Message: "invalid arguments: arguments must be a JSON object"} + } + if err := validate(args); err != nil { + return nil, &inventory.ToolInputError{Message: err.Error()} + } + return json.Marshal(args) + } +} + +func validateTreeArguments(args map[string]any) error { + for _, field := range []string{"owner", "repo"} { + if _, err := RequiredParam[string](args, field); err != nil { + return err + } + } + if _, err := OptionalParam[string](args, "tree_sha"); err != nil { + return err + } + if _, err := OptionalBoolParamWithDefault(args, "recursive", false); err != nil { + return err + } + _, err := OptionalParam[string](args, "path_filter") + return err +} + +func validateListGistsArguments(args map[string]any) error { + if _, err := OptionalParam[string](args, "username"); err != nil { + return err + } + if _, err := OptionalParam[string](args, "since"); err != nil { + return err + } + pagination, err := OptionalPaginationParams(args) + if err != nil { + return err + } + args["page"], args["perPage"] = pagination.Page, pagination.PerPage + return nil +} + +func validateGetGistArguments(args map[string]any) error { + _, err := RequiredParam[string](args, "gist_id") + return err +} + +func validateCreateGistArguments(args map[string]any) error { + if _, err := OptionalParam[string](args, "description"); err != nil { + return err + } + for _, field := range []string{"filename", "content"} { + if _, err := RequiredParam[string](args, field); err != nil { + return err + } + } + _, err := OptionalParam[bool](args, "public") + return err +} + +func validateUpdateGistArguments(args map[string]any) error { + if _, err := RequiredParam[string](args, "gist_id"); err != nil { + return err + } + if _, err := OptionalParam[string](args, "description"); err != nil { + return err + } + for _, field := range []string{"filename", "content"} { + if _, err := RequiredParam[string](args, field); err != nil { + return err + } + } + return nil +} + +func treeOutputSchema() *jsonschema.Schema { + schema := repositoryOutputSchema[TreeResponse]() + // The SDK validates the zero output on error results, so tree stays nullable. + // Size is omitted, never null, for entries without a size. + size := schema.Properties["tree"].Items.Properties["size"] + size.Types, size.Type = nil, "integer" + return schema +} + +func gistOutputSchema() *jsonschema.Schema { + return forbidOmittedNulls(repositoryOutputSchema[github.Gist]()) +} + +func gistListOutputSchema() *jsonschema.Schema { + return forbidOmittedNulls(repositoryOutputSchema[[]*github.Gist]()) +} + +// forbidOmittedNulls removes null from every optional property. go-github +// marks such fields omitempty, so absent values are omitted rather than null. +func forbidOmittedNulls(schema *jsonschema.Schema) *jsonschema.Schema { + if schema == nil { + return nil + } + required := make(map[string]bool, len(schema.Required)) + for _, name := range schema.Required { + required[name] = true + } + for name, property := range schema.Properties { + if !required[name] { + property.Types = slices.DeleteFunc(slices.Clone(property.Types), func(t string) bool { return t == "null" }) + if len(property.Types) == 1 { + property.Type, property.Types = property.Types[0], nil + } + } + forbidOmittedNulls(property) + } + forbidOmittedNulls(schema.Items) + forbidOmittedNulls(schema.AdditionalProperties) + for _, definition := range schema.Defs { + forbidOmittedNulls(definition) + } + return schema +} + +func gistMutationOutputSchema() *jsonschema.Schema { + return repositoryOutputSchema[MinimalResponse]() +} diff --git a/pkg/github/typed_git_gist_outputs_test.go b/pkg/github/typed_git_gist_outputs_test.go new file mode 100644 index 0000000000..386284c48a --- /dev/null +++ b/pkg/github/typed_git_gist_outputs_test.go @@ -0,0 +1,233 @@ +package github + +import ( + "context" + "encoding/json" + "maps" + "net/http" + "strings" + "testing" + "time" + + "github.com/github/github-mcp-server/internal/toolsnaps" + "github.com/github/github-mcp-server/pkg/inventory" + "github.com/github/github-mcp-server/pkg/translations" + "github.com/google/jsonschema-go/jsonschema" + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var typedGitGistProtocols = []string{inventory.ProtocolVersionMultiRoundTrip, "2025-11-25", "", "unknown"} + +func gitGistTypedSession(t *testing.T, deps ToolDependencies, protocol string) (*mcp.ClientSession, map[string]*jsonschema.Resolved) { + t.Helper() + tr := translations.NullTranslationHelper + tools := []inventory.ServerTool{GetRepositoryTree(tr), ListGists(tr), GetGist(tr), CreateGist(tr), UpdateGist(tr)} + inv, err := inventory.NewBuilder().SetTools(tools).WithToolsets([]string{"all"}).Build() + require.NoError(t, err) + server := mcp.NewServer(&mcp.Implementation{Name: "git-gist", Version: "v1"}, nil) + server.AddReceivingMiddleware(InjectDepsMiddleware(deps)) + inv.RegisterTools(context.Background(), server, deps) + if protocol == "" || protocol == "unknown" { + server.AddReceivingMiddleware(func(next mcp.MethodHandler) mcp.MethodHandler { + return func(ctx context.Context, method string, req mcp.Request) (mcp.Result, error) { + switch req := req.(type) { + case *mcp.ListToolsRequest: + req.Params.Meta = mcp.Meta{mcp.MetaKeyProtocolVersion: protocol} + case *mcp.CallToolRequest: + req.Params.Meta = mcp.Meta{mcp.MetaKeyProtocolVersion: protocol} + } + return next(ctx, method, req) + } + }) + } + version := protocol + if version == "" || version == "unknown" { + version = inventory.ProtocolVersionMultiRoundTrip + } + session := connectCommentVisibilityClient(t, server, version) + list, err := session.ListTools(context.Background(), nil) + require.NoError(t, err) + require.Len(t, list.Tools, len(tools)) + schemas := make(map[string]*jsonschema.Resolved) + for _, tool := range list.Tools { + if protocol != inventory.ProtocolVersionMultiRoundTrip { + assert.Nil(t, tool.OutputSchema, tool.Name) + continue + } + require.NotNil(t, tool.OutputSchema, tool.Name) + require.NoError(t, toolsnaps.Test(tool.Name+"_typed", *tool)) + var schema jsonschema.Schema + require.NoError(t, json.Unmarshal([]byte(mustMarshalJSON(t, tool.OutputSchema)), &schema)) + resolved, err := schema.Resolve(nil) + require.NoError(t, err) + schemas[tool.Name] = resolved + } + return session, schemas +} + +type gitGistCase struct { + name string + args map[string]any + method string + path string + query string + body string + status int + text string +} + +func gitGistDeps(t *testing.T, current **gitGistCase, apiError bool) BaseDeps { + t.Helper() + client := &http.Client{Transport: recorderTransport{handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tc := *current + if r.URL.Path == "/repos/owner/repo" { + _, _ = w.Write([]byte(`{"default_branch":"main"}`)) + return + } + assert.Equal(t, tc.method, r.Method) + assert.Equal(t, tc.path, r.URL.Path) + assert.Equal(t, tc.query, r.URL.RawQuery) + if apiError { + w.WriteHeader(http.StatusForbidden) + _, _ = w.Write([]byte(`{"message":"Forbidden"}`)) + return + } + w.WriteHeader(tc.status) + _, _ = w.Write([]byte(tc.body)) + })}} + return BaseDeps{Client: mustNewGHClient(t, client), ContentWindowSize: 100, RepoAccessCache: stubRepoAccessCache(nil, time.Minute)} +} + +const ( + gistBody = `{"id":"abc","description":"d","public":true,"comments":2,"html_url":"https://gist.github.com/abc","created_at":"2026-01-01T00:00:00Z","owner":{"login":"octocat","id":1},"files":{"a.txt":{"filename":"a.txt","size":3,"content":"hey"}}}` + gistText = `{"id":"abc","description":"d","public":true,"owner":{"login":"octocat","id":1},"files":{"a.txt":{"size":3,"filename":"a.txt","content":"hey"}},"comments":2,"html_url":"https://gist.github.com/abc","created_at":"2026-01-01T00:00:00Z"}` + treeBody = `{"sha":"t1","truncated":false,"tree":[{"path":"src/a.go","mode":"100644","type":"blob","sha":"s1","size":5,"url":"u1"},{"path":"src","mode":"040000","type":"tree","sha":"s2","url":"u2"},{"path":"README.md","mode":"100644","type":"blob","sha":"s3","size":1,"url":"u3"}]}` + minimalOK = `{"id":"abc","url":"https://gist.github.com/abc"}` +) + +func gitGistWireCases() []gitGistCase { + return []gitGistCase{ + {name: "get_repository_tree", args: map[string]any{"owner": "owner", "repo": "repo", "recursive": true, "path_filter": "src"}, method: "GET", path: "/repos/owner/repo/git/trees/main", query: "recursive=1", body: treeBody, status: 200, + text: `{"sha":"t1","truncated":false,"tree":[{"path":"src/a.go","type":"blob","size":5,"mode":"100644","sha":"s1","url":"u1"},{"path":"src","type":"tree","mode":"040000","sha":"s2","url":"u2"}],"tree_sha":"main","owner":"owner","repo":"repo","recursive":true,"count":2}`}, + {name: "get_repository_tree", args: map[string]any{"owner": "owner", "repo": "repo", "tree_sha": "abc", "path_filter": "none"}, method: "GET", path: "/repos/owner/repo/git/trees/abc", body: treeBody, status: 200, + text: `{"sha":"t1","truncated":false,"tree":[],"tree_sha":"abc","owner":"owner","repo":"repo","recursive":false,"count":0}`}, + {name: "list_gists", args: map[string]any{"username": "octocat", "since": "2026-01-01T00:00:00Z", "page": "2e0", "perPage": "5"}, method: "GET", path: "/users/octocat/gists", query: "page=2&per_page=5&since=2026-01-01T00%3A00%3A00Z", body: `[` + gistBody + `]`, status: 200, text: `[` + gistText + `]`}, + {name: "list_gists", args: map[string]any{"page": 0, "perPage": 0}, method: "GET", path: "/gists", query: "page=1&per_page=30", body: `[]`, status: 200, text: `[]`}, + {name: "list_gists", args: nil, method: "GET", path: "/gists", query: "page=1&per_page=30", body: `null`, status: 200, text: `null`}, + {name: "get_gist", args: map[string]any{"gist_id": "abc"}, method: "GET", path: "/gists/abc", body: gistBody, status: 200, text: gistText}, + {name: "create_gist", args: map[string]any{"filename": "a.txt", "content": "hey", "description": "d", "public": true}, method: "POST", path: "/gists", body: gistBody, status: 201, text: minimalOK}, + {name: "update_gist", args: map[string]any{"gist_id": "abc", "filename": "a.txt", "content": "hey"}, method: "PATCH", path: "/gists/abc", body: gistBody, status: 200, text: minimalOK}, + } +} + +func assertGitGistResult(t *testing.T, result *mcp.CallToolResult, schema *jsonschema.Resolved, text string, isError bool) { + t.Helper() + require.Equal(t, isError, result.IsError, mustMarshalJSON(t, result)) + require.Len(t, result.Content, 1) + actual := getTextResult(t, result).Text + if isError { + assert.Equal(t, text, actual) + } else { + assert.JSONEq(t, text, actual) + } + if schema == nil || isError { + assert.Nil(t, result.StructuredContent) + return + } + var output any + require.NoError(t, json.Unmarshal([]byte(mustMarshalJSON(t, result.StructuredContent)), &output)) + require.NoError(t, schema.Validate(output)) + assert.JSONEq(t, text, mustMarshalJSON(t, output)) +} + +func TestTypedGitGistWireOutputs(t *testing.T) { + for _, protocol := range typedGitGistProtocols { + t.Run("protocol="+protocol, func(t *testing.T) { + var current *gitGistCase + session, schemas := gitGistTypedSession(t, gitGistDeps(t, ¤t, false), protocol) + for _, tc := range gitGistWireCases() { + t.Run(tc.name+"/"+mustMarshalJSON(t, tc.args), func(t *testing.T) { + current = &tc + args := map[string]any{} + if tc.name == "get_repository_tree" { + args["owner"], args["repo"] = "owner", "repo" + } + maps.Copy(args, tc.args) + result, err := session.CallTool(context.Background(), &mcp.CallToolParams{Name: tc.name, Arguments: args}) + require.NoError(t, err) + assertGitGistResult(t, result, schemas[tc.name], tc.text, false) + }) + } + }) + } +} + +func TestTypedGitGistWireErrors(t *testing.T) { + cases := []gitGistCase{ + {name: "get_repository_tree", args: map[string]any{"owner": "", "repo": "r"}, text: "missing required parameter: owner"}, + {name: "get_repository_tree", args: map[string]any{"owner": "o", "repo": "r", "recursive": "true"}, text: "parameter recursive is not of type bool, is string"}, + {name: "get_repository_tree", args: map[string]any{"owner": "o", "repo": "r", "tree_sha": 1}, text: "parameter tree_sha is not of type string, is float64"}, + {name: "list_gists", args: map[string]any{"username": 1}, text: "parameter username is not of type string, is float64"}, + {name: "list_gists", args: map[string]any{"since": "bad"}, text: `invalid since timestamp: invalid ISO 8601 timestamp: bad`}, + {name: "list_gists", args: map[string]any{"page": "bad"}, text: "parameter page is not a valid number: invalid numeric value: bad"}, + {name: "get_gist", args: map[string]any{}, text: "missing required parameter: gist_id"}, + {name: "create_gist", args: map[string]any{"content": "x"}, text: "missing required parameter: filename"}, + {name: "create_gist", args: map[string]any{"filename": "f", "content": "x", "public": "yes"}, text: "parameter public is not of type bool, is string"}, + {name: "update_gist", args: map[string]any{"gist_id": "g", "description": false, "filename": "f", "content": "c"}, text: "parameter description is not of type string, is bool"}, + } + for _, protocol := range typedGitGistProtocols { + t.Run("protocol="+protocol, func(t *testing.T) { + var current *gitGistCase + session, _ := gitGistTypedSession(t, gitGistDeps(t, ¤t, false), protocol) + for _, tc := range cases { + t.Run(tc.name+"/"+tc.text, func(t *testing.T) { + result, err := session.CallTool(context.Background(), &mcp.CallToolParams{Name: tc.name, Arguments: tc.args}) + require.NoError(t, err) + if strings.HasPrefix(tc.text, "invalid since") { + require.True(t, result.IsError) + assert.Contains(t, getTextResult(t, result).Text, "invalid since timestamp") + assert.Nil(t, result.StructuredContent) + return + } + assertGitGistResult(t, result, nil, tc.text, true) + }) + } + session, _ = gitGistTypedSession(t, gitGistDeps(t, ¤t, true), protocol) + for _, tc := range gitGistWireCases() { + current = &tc + args := map[string]any{"owner": "owner", "repo": "repo"} + if tc.name != "get_repository_tree" { + args = map[string]any{} + } + maps.Copy(args, tc.args) + result, err := session.CallTool(context.Background(), &mcp.CallToolParams{Name: tc.name, Arguments: args}) + require.NoError(t, err, tc.name+mustMarshalJSON(t, tc.args)) + require.True(t, result.IsError, tc.name) + assert.Contains(t, getTextResult(t, result).Text, "Forbidden") + assert.Nil(t, result.StructuredContent) + } + }) + } +} + +func TestGitGistOutputSchemasRejectMismatches(t *testing.T) { + for name, tc := range map[string]struct { + schema *jsonschema.Schema + bad string + }{ + "tree": {treeOutputSchema(), `{"sha":"s","truncated":false,"tree":[{"path":"p","type":"blob","mode":"m","sha":"s","url":"u","size":null}],"tree_sha":"t","owner":"o","repo":"r","recursive":false,"count":1}`}, + "gist": {gistOutputSchema(), `{"id":null}`}, + "list": {gistListOutputSchema(), `[{"files":{"a":{"size":"3"}}}]`}, + "create": {gistMutationOutputSchema(), `{"id":"a"}`}, + } { + t.Run(name, func(t *testing.T) { + resolved, err := tc.schema.Resolve(nil) + require.NoError(t, err) + var value any + require.NoError(t, json.Unmarshal([]byte(tc.bad), &value)) + require.Error(t, resolved.Validate(value)) + }) + } +}