Skip to content
119 changes: 119 additions & 0 deletions src/mcp_github/github_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,3 +665,122 @@ def create_release(self, repo_owner: str, repo_name: str, tag_name: str, release
logging.error(f"Error creating release: {str(e)}")
traceback.print_exc()
return {"status": "error", "message": str(e)}

def user_activity_query(self, variables: dict[str, Any], query: str) -> Dict[str, Any]:
"""
Performs a user activity query using GitHub's GraphQL API for the authenticated user (token owner).

**Critical**: To query activities within a specific organization (e.g., "saidsef"):
1. Query the organization DIRECTLY using `organization(login: "saidsef")`
2. Do NOT use `viewer.contributionsCollection` as it excludes many private org activities
3. Organization name is CASE-SENSITIVE - must match exactly

**For Organization-Specific Activity** (e.g., saidsef):
```graphql
query($orgName: String!, $from: GitTimestamp!, $to: GitTimestamp!) {
organization(login: $orgName) {
login
repositories(first: 100, privacy: PRIVATE, orderBy: {field: PUSHED_AT, direction: DESC}) {
nodes {
name
isPrivate
owner { login }
defaultBranchRef {
target {
... on Commit {
history(since: $from, until: $to) {
totalCount
nodes {
author { user { login } }
committedDate
message
}
}
}
}
}
pullRequests(first: 100, states: [OPEN, CLOSED, MERGED], orderBy: {field: UPDATED_AT, direction: DESC}) {
nodes {
number
title
author { login }
createdAt
state
}
}
issues(first: 100, states: [OPEN, CLOSED], orderBy: {field: UPDATED_AT, direction: DESC}) {
nodes {
number
title
author { login }
createdAt
state
}
}
}
}
}
}
```

**For Authenticated User Activity Across All Orgs**:
```graphql
query($from: DateTime!, $to: DateTime!) {
viewer {
login
contributionsCollection(from: $from, to: $to) {
commitContributionsByRepository(maxRepositories: 100) {
repository { name isPrivate owner { login } }
contributions { totalCount }
}
pullRequestContributionsByRepository(maxRepositories: 100) {
repository { name isPrivate owner { login } }
contributions { totalCount }
}
}
organizations(first: 100) {
nodes { login }
}
}
}
```

Args:
variables (dict[str, Any]): Query variables. Options:
- For org-specific with commit history: {"orgName": "saidsef", "from": "2024-10-01T00:00:00Z", "to": "2024-10-31T23:59:59Z"}
Note: Use GitTimestamp type (ISO 8601 format) for $from/$to when querying commit history
- For all activity: {"from": "2024-10-01T00:00:00Z", "to": "2024-10-31T23:59:59Z"}
Note: Use DateTime type for contributionsCollection queries
query (str): GraphQL query string. Use `organization(login: $orgName)` for specific org queries.
IMPORTANT: Declare variables as `GitTimestamp!` for commit history, `DateTime!` for contributionsCollection.

Returns:
Dict[str, Any]: The JSON response from GitHub's GraphQL API containing activity data.

Note: Organization queries require `read:org` scope. Organization name is case-sensitive.
GitTimestamp and DateTime both accept ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ).
"""
logging.info(f"Performing GraphQL query with variables: {variables}")

try:
response = requests.post(
'https://api.github.com/graphql',
json={'query': query, 'variables': variables},
headers=self._get_headers(),
timeout=TIMEOUT
)
response.raise_for_status()
query_data = response.json()

if 'errors' in query_data:
logging.error(f"GraphQL errors: {query_data['errors']}")

return query_data
except requests.exceptions.RequestException as req_err:
logging.error(f"Request error during user activity query: {str(req_err)}")
traceback.print_exc()
return {"status": "error", "message": str(req_err)}
except Exception as e:
logging.error(f"Error performing user activity query: {str(e)}")
traceback.print_exc()
return {"status": "error", "message": str(e)}