Spaces:
Paused
Paused
Upload folder using huggingface_hub
Browse files- api/analysis_api.txt +367 -0
- app.py +25 -6
- mcp/mcp_analysis.py +249 -0
- mcp/servers.txt +29 -29
api/analysis_api.txt
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Analysis API
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
Introduction
|
| 6 |
+
The Analysis API lets you programmatically access Analysis's capabilities to automate and enhance your software development lifecycle. You can use the API to create custom workflows, automate tasks like bug fixing and code reviews, and embed Analysis's intelligence directly into the tools you use every day, such as Slack, Linear, and GitHub.
|
| 7 |
+
|
| 8 |
+
Note: The Analysis API is in an alpha release, which means it is experimental. Be aware that we may change specifications, API keys, and definitions as we work toward stabilization. In the future, we plan to maintain at least one stable and one experimental version.
|
| 9 |
+
|
| 10 |
+
Authentication
|
| 11 |
+
To get started with the Analysis API, you'll need an API key.
|
| 12 |
+
|
| 13 |
+
Generate Your API Key
|
| 14 |
+
In the Analysis web app, go to the Settings page to create a new API key. You can have at most 3 API keys at a time.
|
| 15 |
+
|
| 16 |
+
Analysis API Key creation interface
|
| 17 |
+
Use Your API Key
|
| 18 |
+
To authenticate your requests, pass the API key in the X-Goog-Api-Key header of your API calls.
|
| 19 |
+
|
| 20 |
+
Important: Keep your API keys secure. Don't share them or embed them in public code. For your protection, any API keys found to be publicly exposed will be automatically disabled to prevent abuse.
|
| 21 |
+
|
| 22 |
+
API concepts
|
| 23 |
+
The Analysis API is built around a few core resources. Understanding these will help you use the API effectively.
|
| 24 |
+
|
| 25 |
+
Source
|
| 26 |
+
An input source for the agent (e.g., a GitHub repository). Before using a source using the API, you must first install the Analysis GitHub app through the Analysis web app.
|
| 27 |
+
Session
|
| 28 |
+
A continuous unit of work within a specific context, similar to a chat session. A session is initiated with a prompt and a source.
|
| 29 |
+
Activity
|
| 30 |
+
A single unit of work within a Session. A Session contains multiple activities from both the user and the agent, such as generating a plan, sending a message, or updating progress.
|
| 31 |
+
Quickstart: Your first API call
|
| 32 |
+
We'll walk through creating your first session with the Analysis API using curl.
|
| 33 |
+
|
| 34 |
+
Step 1: List your available sources
|
| 35 |
+
First, you need to find the name of the source you want to work with (e.g., your GitHub repo). This command will return a list of all sources you have connected to Analysis.
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
curl 'https://jules.googleapis.com/v1alpha/sources' \
|
| 39 |
+
-H 'X-Goog-Api-Key: YOUR_API_KEY'
|
| 40 |
+
The response will look something like this:
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
{
|
| 44 |
+
"sources": [
|
| 45 |
+
{
|
| 46 |
+
"name": "sources/github/bobalover/boba",
|
| 47 |
+
"id": "github/bobalover/boba",
|
| 48 |
+
"githubRepo": {
|
| 49 |
+
"owner": "bobalover",
|
| 50 |
+
"repo": "boba"
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
],
|
| 54 |
+
"nextPageToken": "github/bobalover/boba-web"
|
| 55 |
+
}
|
| 56 |
+
Step 2: Create a new session
|
| 57 |
+
Now, create a new session. You'll need the source name from the previous step. This request tells Analysis to create a boba app in the specified repository.
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
curl 'https://jules.googleapis.com/v1alpha/sessions' \
|
| 61 |
+
-X POST \
|
| 62 |
+
-H "Content-Type: application/json" \
|
| 63 |
+
-H 'X-Goog-Api-Key: YOUR_API_KEY' \
|
| 64 |
+
-d '{
|
| 65 |
+
"prompt": "Create a boba app!",
|
| 66 |
+
"sourceContext": {
|
| 67 |
+
"source": "sources/github/bobalover/boba",
|
| 68 |
+
"githubRepoContext": {
|
| 69 |
+
"startingBranch": "main"
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"automationMode": "AUTO_CREATE_PR",
|
| 73 |
+
"title": "Boba App"
|
| 74 |
+
}'
|
| 75 |
+
The automationMode field is optional. By default, no PR will be automatically created.
|
| 76 |
+
|
| 77 |
+
The immediate response will look something like this:
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
{
|
| 81 |
+
"name": "sessions/31415926535897932384",
|
| 82 |
+
"id": "31415926535897932384",
|
| 83 |
+
"title": "Boba App",
|
| 84 |
+
"sourceContext": {
|
| 85 |
+
"source": "sources/github/bobalover/boba",
|
| 86 |
+
"githubRepoContext": {
|
| 87 |
+
"startingBranch": "main"
|
| 88 |
+
}
|
| 89 |
+
},
|
| 90 |
+
"prompt": "Create a boba app!"
|
| 91 |
+
}
|
| 92 |
+
You can poll the latest session information using GetSession or ListSessions. For example, if a PR was automatically created, you can see the PR in the session output.
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
{
|
| 96 |
+
"name": "sessions/31415926535897932384",
|
| 97 |
+
"id": "31415926535897932384",
|
| 98 |
+
"title": "Boba App",
|
| 99 |
+
"sourceContext": {
|
| 100 |
+
"source": "sources/github/bobalover/boba",
|
| 101 |
+
"githubRepoContext": {
|
| 102 |
+
"startingBranch": "main"
|
| 103 |
+
}
|
| 104 |
+
},
|
| 105 |
+
"prompt": "Create a boba app!",
|
| 106 |
+
"outputs": [
|
| 107 |
+
{
|
| 108 |
+
"pullRequest": {
|
| 109 |
+
"url": "https://github.com/bobalover/boba/pull/35",
|
| 110 |
+
"title": "Create a boba app",
|
| 111 |
+
"description": "This change adds the initial implementation of a boba app."
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
]
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
By default, sessions created through the API will have their plans automatically approved. If you want to create a session that requires explicit plan approval, set the requirePlanApproval field to true.
|
| 118 |
+
|
| 119 |
+
Step 3: Listing sessions
|
| 120 |
+
You can list your sessions as follows.
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
curl 'https://jules.googleapis.com/v1alpha/sessions?pageSize=5' \
|
| 124 |
+
-H 'X-Goog-Api-Key: YOUR_API_KEY'
|
| 125 |
+
Step 4: Approve plan
|
| 126 |
+
If your session requires explicit plan approval, you can approve the latest plan as follows:
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
curl 'https://jules.googleapis.com/v1alpha/sessions/SESSION_ID:approvePlan' \
|
| 130 |
+
-X POST \
|
| 131 |
+
-H "Content-Type: application/json" \
|
| 132 |
+
-H 'X-Goog-Api-Key: YOUR_API_KEY'
|
| 133 |
+
Step 5: Activities and interacting with the agent
|
| 134 |
+
To list activities in a session:
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
curl 'https://jules.googleapis.com/v1alpha/sessions/SESSION_ID/activities?pageSize=30' \
|
| 138 |
+
-H 'X-Goog-Api-Key: YOUR_API_KEY'
|
| 139 |
+
To send a message to the agent:
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
curl 'https://jules.googleapis.com/v1alpha/sessions/SESSION_ID:sendMessage' \
|
| 143 |
+
-X POST \
|
| 144 |
+
-H "Content-Type: application/json" \
|
| 145 |
+
-H 'X-Goog-Api-Key: YOUR_API_KEY' \
|
| 146 |
+
-d '{
|
| 147 |
+
"prompt": "Can you make the app corgi themed?"
|
| 148 |
+
}'
|
| 149 |
+
The response will be empty because the agent will send its response in the next activity. To see the agent's response, list the activities again.
|
| 150 |
+
Here is an example of a ListActivities response.
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
{
|
| 154 |
+
"activities": [
|
| 155 |
+
{
|
| 156 |
+
"name": "sessions/14550388554331055113/activities/02200cce44f746308651037e4a18caed",
|
| 157 |
+
"createTime": "2025-10-03T05:43:42.801654Z",
|
| 158 |
+
"originator": "agent",
|
| 159 |
+
"planGenerated": {
|
| 160 |
+
"plan": {
|
| 161 |
+
"id": "5103d604240042cd9f59a4cb2355643a",
|
| 162 |
+
"steps": [
|
| 163 |
+
{
|
| 164 |
+
"id": "705a61fc8ec24a98abc9296a3956fb6b",
|
| 165 |
+
"title": "Setup the environment. I will install the dependencies to run the app."
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"id": "bb5276efad354794a4527e9ad7c0cd42",
|
| 169 |
+
"title": "Modify `src/App.js`. I will replace the existing React boilerplate with a simple Boba-themed component. This will include a title and a list of boba options.",
|
| 170 |
+
"index": 1
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"id": "377c9a1c91764dc794a618a06772e3d8",
|
| 174 |
+
"title": "Modify `src/App.css`. I will update the CSS to provide a fresh, modern look for the Boba app.",
|
| 175 |
+
"index": 2
|
| 176 |
+
},
|
| 177 |
+
{
|
| 178 |
+
"id": "335802b585b449aeabb855c722cd9c40",
|
| 179 |
+
"title": "Frontend Verification. I will use the `frontend_verification_instructions` tool to get instructions on how to write a Playwright script to verify the frontend application and generate a screenshot of the changes.",
|
| 180 |
+
"index": 3
|
| 181 |
+
},
|
| 182 |
+
{
|
| 183 |
+
"id": "3e4cc97c7b2448668d1ac75b8c7b7d69",
|
| 184 |
+
"title": "Submit the changes. Once the app is looking good and verified, I will submit my work.",
|
| 185 |
+
"index": 4
|
| 186 |
+
}
|
| 187 |
+
]
|
| 188 |
+
}
|
| 189 |
+
},
|
| 190 |
+
"id": "02200cce44f746308651037e4a18caed"
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"name": "sessions/14550388554331055113/activities/2918fac8bc54450a9cbda423b7688413",
|
| 194 |
+
"createTime": "2025-10-03T05:43:44.954030Z",
|
| 195 |
+
"originator": "user",
|
| 196 |
+
"planApproved": {
|
| 197 |
+
"planId": "5103d604240042cd9f59a4cb2355643a"
|
| 198 |
+
},
|
| 199 |
+
"id": "2918fac8bc54450a9cbda423b7688413"
|
| 200 |
+
},
|
| 201 |
+
{
|
| 202 |
+
"name": "sessions/14550388554331055113/activities/5b3acd1b3ca2439f9cbaefaccf7f709a",
|
| 203 |
+
"createTime": "2025-10-03T05:44:16.700231Z",
|
| 204 |
+
"originator": "agent",
|
| 205 |
+
"progressUpdated": {
|
| 206 |
+
"title": "Ran bash command",
|
| 207 |
+
"description": "Command: \nnpm install\nOutput: added 1326 packages, and audited 1327 packages in 25s\n\n268 packages are looking for fundingExit Code: 0"
|
| 208 |
+
},
|
| 209 |
+
"artifacts": [
|
| 210 |
+
{
|
| 211 |
+
"bashOutput": {
|
| 212 |
+
"command": "\nnpm install",
|
| 213 |
+
"output": "added 1326 packages, and audited 1327 packages in 25s\n\n268 packages are looking for funding"
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
],
|
| 217 |
+
"id": "5b3acd1b3ca2439f9cbaefaccf7f709a"
|
| 218 |
+
},
|
| 219 |
+
{
|
| 220 |
+
"name": "sessions/14550388554331055113/activities/1ad545d812614a5b84c23a33f6120063",
|
| 221 |
+
"createTime": "2025-10-03T05:44:19.502115Z",
|
| 222 |
+
"originator": "agent",
|
| 223 |
+
"progressUpdated": {
|
| 224 |
+
"title": "I have installed the dependencies using `npm install`."
|
| 225 |
+
},
|
| 226 |
+
"artifacts": [
|
| 227 |
+
{
|
| 228 |
+
"changeSet": {
|
| 229 |
+
"source": "sources/github/bobalover/boba",
|
| 230 |
+
"gitPatch": {
|
| 231 |
+
"baseCommitId": "36ead0a4caefc451b9652ed926a15af9570f4f35"
|
| 232 |
+
}
|
| 233 |
+
}
|
| 234 |
+
}
|
| 235 |
+
],
|
| 236 |
+
"id": "1ad545d812614a5b84c23a33f6120063"
|
| 237 |
+
},
|
| 238 |
+
{
|
| 239 |
+
"name": "sessions/14550388554331055113/activities/3a2b46329f894ebea1faf6b8fb956428",
|
| 240 |
+
"createTime": "2025-10-03T05:44:29.265425Z",
|
| 241 |
+
"originator": "agent",
|
| 242 |
+
"progressUpdated": {
|
| 243 |
+
"title": "I have replaced the boilerplate in `src/App.js` with a Boba-themed component."
|
| 244 |
+
},
|
| 245 |
+
"artifacts": [
|
| 246 |
+
{
|
| 247 |
+
"changeSet": {
|
| 248 |
+
"source": "sources/github/bobalover/boba",
|
| 249 |
+
"gitPatch": {
|
| 250 |
+
"unidiffPatch": "",
|
| 251 |
+
"baseCommitId": "36ead0a4caefc451b9652ed926a15af9570f4f35"
|
| 252 |
+
}
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
],
|
| 256 |
+
"id": "3a2b46329f894ebea1faf6b8fb956428"
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"name": "sessions/14550388554331055113/activities/4e8dd66f15e247e3ae5e48eeff89ef47",
|
| 260 |
+
"createTime": "2025-10-03T05:44:37.721013Z",
|
| 261 |
+
"originator": "agent",
|
| 262 |
+
"progressUpdated": {
|
| 263 |
+
"title": "I have updated the CSS in `src/App.css` to give the app a fresh, modern look."
|
| 264 |
+
},
|
| 265 |
+
"artifacts": [
|
| 266 |
+
{
|
| 267 |
+
"changeSet": {
|
| 268 |
+
"source": "sources/github/bobalover/boba",
|
| 269 |
+
"gitPatch": {
|
| 270 |
+
"unidiffPatch": ""
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
}
|
| 274 |
+
],
|
| 275 |
+
"id": "4e8dd66f15e247e3ae5e48eeff89ef47"
|
| 276 |
+
},
|
| 277 |
+
{
|
| 278 |
+
"name": "sessions/14550388554331055113/activities/100901155a4141d3b37e8e8d2950f3b7",
|
| 279 |
+
"createTime": "2025-10-03T05:44:51.048996Z",
|
| 280 |
+
"originator": "agent",
|
| 281 |
+
"progressUpdated": {
|
| 282 |
+
"title": "Ran bash command",
|
| 283 |
+
"description": "Command: \nOutput: Command failed due to an internal error.\nExit Code: 1"
|
| 284 |
+
},
|
| 285 |
+
"artifacts": [
|
| 286 |
+
{
|
| 287 |
+
"bashOutput": {
|
| 288 |
+
"output": "Command failed due to an internal error.",
|
| 289 |
+
"exitCode": 1
|
| 290 |
+
}
|
| 291 |
+
}
|
| 292 |
+
],
|
| 293 |
+
"id": "100901155a4141d3b37e8e8d2950f3b7"
|
| 294 |
+
},
|
| 295 |
+
{
|
| 296 |
+
"name": "sessions/14550388554331055113/activities/a76b35353eda42d09b1c37aedaa56047",
|
| 297 |
+
"createTime": "2025-10-03T05:47:49.628363Z",
|
| 298 |
+
"originator": "agent",
|
| 299 |
+
"progressUpdated": {
|
| 300 |
+
"title": "Frontend verification",
|
| 301 |
+
"description": "Agent provided UI verification."
|
| 302 |
+
},
|
| 303 |
+
"artifacts": [
|
| 304 |
+
{
|
| 305 |
+
"media": {
|
| 306 |
+
"data": "",
|
| 307 |
+
"mimeType": "image/png"
|
| 308 |
+
}
|
| 309 |
+
}
|
| 310 |
+
],
|
| 311 |
+
"id": "a76b35353eda42d09b1c37aedaa56047"
|
| 312 |
+
},
|
| 313 |
+
{
|
| 314 |
+
"name": "sessions/14550388554331055113/activities/db089c7052024cbeb9e37b8c584bc964",
|
| 315 |
+
"createTime": "2025-10-03T05:47:53.669642Z",
|
| 316 |
+
"originator": "agent",
|
| 317 |
+
"progressUpdated": {
|
| 318 |
+
"title": "I have successfully verified the frontend changes by building the app, runnin...",
|
| 319 |
+
"description": "I have successfully verified the frontend changes by building the app, running a Playwright script, and inspecting the resulting screenshot."
|
| 320 |
+
},
|
| 321 |
+
"artifacts": [
|
| 322 |
+
{
|
| 323 |
+
"changeSet": {
|
| 324 |
+
"source": "sources/github/bobalover/boba",
|
| 325 |
+
"gitPatch": {
|
| 326 |
+
"unidiffPatch": "",
|
| 327 |
+
"baseCommitId": "36ead0a4caefc451b9652ed926a15af9570f4f35"
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
}
|
| 331 |
+
],
|
| 332 |
+
"id": "db089c7052024cbeb9e37b8c584bc964"
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"name": "sessions/14550388554331055113/activities/890e16e30dbb4bf99a92613bdccec212",
|
| 336 |
+
"createTime": "2025-10-03T05:48:14.434427Z",
|
| 337 |
+
"originator": "agent",
|
| 338 |
+
"progressUpdated": {
|
| 339 |
+
"title": "Code reviewed",
|
| 340 |
+
"description": "The user wants to create a \"boba app\". This is a very open-ended request, but the agent's implementation suggests the goal is to create a simple, static web page about boba."
|
| 341 |
+
},
|
| 342 |
+
"id": "890e16e30dbb4bf99a92613bdccec212"
|
| 343 |
+
},
|
| 344 |
+
{
|
| 345 |
+
"name": "sessions/14550388554331055113/activities/022837dbc0e940eabcc1bc53608e15fc",
|
| 346 |
+
"createTime": "2025-10-03T05:48:35.523200Z",
|
| 347 |
+
"originator": "agent",
|
| 348 |
+
"sessionCompleted": {},
|
| 349 |
+
"artifacts": [
|
| 350 |
+
{
|
| 351 |
+
"changeSet": {
|
| 352 |
+
"source": "sources/github/bobalover/boba",
|
| 353 |
+
"gitPatch": {
|
| 354 |
+
"unidiffPatch": "",
|
| 355 |
+
"baseCommitId": "36ead0a4caefc451b9652ed926a15af9570f4f35",
|
| 356 |
+
"suggestedCommitMessage": "feat: Create simple Boba App\n\nThis commit transforms the default Create React App boilerplate into a simple, visually appealing Boba-themed application."
|
| 357 |
+
}
|
| 358 |
+
}
|
| 359 |
+
}
|
| 360 |
+
],
|
| 361 |
+
"id": "022837dbc0e940eabcc1bc53608e15fc"
|
| 362 |
+
}
|
| 363 |
+
]
|
| 364 |
+
}
|
| 365 |
+
Full API reference
|
| 366 |
+
View the full API reference documentation for the Analysis API.
|
| 367 |
+
|
app.py
CHANGED
|
@@ -158,7 +158,7 @@ except ImportError as e:
|
|
| 158 |
|
| 159 |
# Configuration from environment variables
|
| 160 |
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") or os.environ.get("GITHUB_API_TOKEN") or os.environ.get("GITHUB_API_KEY")
|
| 161 |
-
ANALYSIS_API_KEY = os.environ.get("
|
| 162 |
BLABLADOR_API_KEY = os.environ.get("BLABLADOR_API_KEY")
|
| 163 |
BLABLADOR_BASE_URL = "https://api.helmholtz-blablador.fz-juelich.de/v1"
|
| 164 |
ANALYSIS_API_URL = "https://jules.googleapis.com/v1alpha"
|
|
@@ -918,12 +918,11 @@ def render_slides(repo_full_name, branch_name, report_path):
|
|
| 918 |
|
| 919 |
if os.path.exists(f"{output_dir}/index.html"):
|
| 920 |
# Return IFrame pointing to the generated site.
|
| 921 |
-
# Use
|
| 922 |
abspath = os.path.abspath(f"{output_dir}/index.html")
|
| 923 |
add_log(f"Slides rendered successfully: {abspath}")
|
| 924 |
|
| 925 |
-
|
| 926 |
-
return f'<iframe src="file/{abspath}" width="100%" height="600px" frameborder="0"></iframe>'
|
| 927 |
else:
|
| 928 |
add_log(f"ERROR: mkslides finished but {output_dir}/index.html not found.")
|
| 929 |
return "Failed to render slides: index.html not found."
|
|
@@ -1054,7 +1053,12 @@ with gr.Blocks(title="UX Analysis Orchestrator") as demo:
|
|
| 1054 |
|
| 1055 |
def sl_update_reports(repo_name, branch_name):
|
| 1056 |
reports = get_reports_in_branch(repo_name, branch_name, filter_type="slides")
|
| 1057 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1058 |
|
| 1059 |
sl_repo_select.change(fn=sl_update_branches, inputs=[sl_repo_select], outputs=[sl_branch_select])
|
| 1060 |
|
|
@@ -1079,8 +1083,23 @@ with gr.Blocks(title="UX Analysis Orchestrator") as demo:
|
|
| 1079 |
return html, new_idx, counter_text
|
| 1080 |
|
| 1081 |
sl_refresh_branches_btn.click(fn=sl_update_branches, inputs=[sl_repo_select], outputs=[sl_branch_select])
|
| 1082 |
-
sl_branch_select.change(fn=sl_update_reports, inputs=[sl_repo_select, sl_branch_select], outputs=[sl_report_select, all_decks_state])
|
| 1083 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1084 |
sl_render_btn.click(fn=sl_render_wrapper, inputs=[sl_repo_select, sl_branch_select, sl_report_select, sl_manual_path], outputs=[slideshow_display, carousel_controls])
|
| 1085 |
|
| 1086 |
sl_render_all_btn.click(fn=start_carousel, inputs=[sl_repo_select, sl_branch_select, all_decks_state], outputs=[slideshow_display, carousel_controls, current_deck_idx, deck_counter])
|
|
|
|
| 158 |
|
| 159 |
# Configuration from environment variables
|
| 160 |
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") or os.environ.get("GITHUB_API_TOKEN") or os.environ.get("GITHUB_API_KEY")
|
| 161 |
+
ANALYSIS_API_KEY = os.environ.get("ANALYSIS_API_KEY") or os.environ.get("JULES_API_KEY")
|
| 162 |
BLABLADOR_API_KEY = os.environ.get("BLABLADOR_API_KEY")
|
| 163 |
BLABLADOR_BASE_URL = "https://api.helmholtz-blablador.fz-juelich.de/v1"
|
| 164 |
ANALYSIS_API_URL = "https://jules.googleapis.com/v1alpha"
|
|
|
|
| 918 |
|
| 919 |
if os.path.exists(f"{output_dir}/index.html"):
|
| 920 |
# Return IFrame pointing to the generated site.
|
| 921 |
+
# Use standard Gradio 4 /file= format with absolute path
|
| 922 |
abspath = os.path.abspath(f"{output_dir}/index.html")
|
| 923 |
add_log(f"Slides rendered successfully: {abspath}")
|
| 924 |
|
| 925 |
+
return f'<iframe src="/file={abspath}" width="100%" height="600px" frameborder="0"></iframe>'
|
|
|
|
| 926 |
else:
|
| 927 |
add_log(f"ERROR: mkslides finished but {output_dir}/index.html not found.")
|
| 928 |
return "Failed to render slides: index.html not found."
|
|
|
|
| 1053 |
|
| 1054 |
def sl_update_reports(repo_name, branch_name):
|
| 1055 |
reports = get_reports_in_branch(repo_name, branch_name, filter_type="slides")
|
| 1056 |
+
default_val = None
|
| 1057 |
+
if "user_experience_reports/slides" in reports:
|
| 1058 |
+
default_val = "user_experience_reports/slides"
|
| 1059 |
+
elif reports:
|
| 1060 |
+
default_val = reports[0]
|
| 1061 |
+
return gr.update(choices=reports, value=default_val), reports
|
| 1062 |
|
| 1063 |
sl_repo_select.change(fn=sl_update_branches, inputs=[sl_repo_select], outputs=[sl_branch_select])
|
| 1064 |
|
|
|
|
| 1083 |
return html, new_idx, counter_text
|
| 1084 |
|
| 1085 |
sl_refresh_branches_btn.click(fn=sl_update_branches, inputs=[sl_repo_select], outputs=[sl_branch_select])
|
|
|
|
| 1086 |
|
| 1087 |
+
sl_branch_select.change(
|
| 1088 |
+
fn=sl_update_reports,
|
| 1089 |
+
inputs=[sl_repo_select, sl_branch_select],
|
| 1090 |
+
outputs=[sl_report_select, all_decks_state]
|
| 1091 |
+
).then(
|
| 1092 |
+
fn=sl_render_wrapper,
|
| 1093 |
+
inputs=[sl_repo_select, sl_branch_select, sl_report_select, sl_manual_path],
|
| 1094 |
+
outputs=[slideshow_display, carousel_controls]
|
| 1095 |
+
)
|
| 1096 |
+
|
| 1097 |
+
sl_report_select.change(
|
| 1098 |
+
fn=sl_render_wrapper,
|
| 1099 |
+
inputs=[sl_repo_select, sl_branch_select, sl_report_select, sl_manual_path],
|
| 1100 |
+
outputs=[slideshow_display, carousel_controls]
|
| 1101 |
+
)
|
| 1102 |
+
|
| 1103 |
sl_render_btn.click(fn=sl_render_wrapper, inputs=[sl_repo_select, sl_branch_select, sl_report_select, sl_manual_path], outputs=[slideshow_display, carousel_controls])
|
| 1104 |
|
| 1105 |
sl_render_all_btn.click(fn=start_carousel, inputs=[sl_repo_select, sl_branch_select, all_decks_state], outputs=[slideshow_display, carousel_controls, current_deck_idx, deck_counter])
|
mcp/mcp_analysis.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from mcp.server.fastmcp import FastMCP
|
| 2 |
+
import httpx
|
| 3 |
+
import os
|
| 4 |
+
import logging
|
| 5 |
+
import json
|
| 6 |
+
import subprocess
|
| 7 |
+
import asyncio
|
| 8 |
+
import re
|
| 9 |
+
|
| 10 |
+
# Configure logging
|
| 11 |
+
logging.basicConfig(level=logging.INFO)
|
| 12 |
+
logger = logging.getLogger("mcp-analysis")
|
| 13 |
+
|
| 14 |
+
mcp = FastMCP("analysis")
|
| 15 |
+
|
| 16 |
+
API_BASE_URL = "https://analysis.googleapis.com/v1alpha"
|
| 17 |
+
|
| 18 |
+
class AnalysisClient:
|
| 19 |
+
def __init__(self, api_key: str):
|
| 20 |
+
self.api_key = api_key
|
| 21 |
+
self.base_url = API_BASE_URL
|
| 22 |
+
self.headers = {
|
| 23 |
+
"Content-Type": "application/json",
|
| 24 |
+
"x-goog-api-key": self.api_key
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
async def _request(self, method: str, path: str, **kwargs):
|
| 28 |
+
# Handle session_id prefixing
|
| 29 |
+
if path.startswith("/sessions/sessions/"):
|
| 30 |
+
path = path.replace("/sessions/sessions/", "/sessions/")
|
| 31 |
+
|
| 32 |
+
url = f"{self.base_url}{path}"
|
| 33 |
+
async with httpx.AsyncClient() as client:
|
| 34 |
+
response = await client.request(method, url, headers=self.headers, **kwargs)
|
| 35 |
+
if response.status_code not in (200, 204):
|
| 36 |
+
logger.error(f"Jules API Error ({method} {path}): {response.status_code} - {response.text}")
|
| 37 |
+
response.raise_for_status()
|
| 38 |
+
return response.json() if response.text else {}
|
| 39 |
+
|
| 40 |
+
async def list_sources(self):
|
| 41 |
+
return await self._request("GET", "/sources")
|
| 42 |
+
|
| 43 |
+
async def get_source(self, name: str):
|
| 44 |
+
if not name.startswith("sources/"): name = f"sources/{name}"
|
| 45 |
+
return await self._request("GET", f"/{name}")
|
| 46 |
+
|
| 47 |
+
async def create_session(self, prompt, source, branch="main", title="API Session", automation="AUTO_CREATE_PR"):
|
| 48 |
+
# Format source for API if it is just owner/repo
|
| 49 |
+
if "/" in source and "sources/github/" not in source:
|
| 50 |
+
source = f"sources/github/{source}"
|
| 51 |
+
|
| 52 |
+
payload = {
|
| 53 |
+
"prompt": prompt,
|
| 54 |
+
"sourceContext": {
|
| 55 |
+
"source": source,
|
| 56 |
+
"githubRepoContext": {"startingBranch": branch}
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
if title: payload["title"] = title
|
| 60 |
+
if automation: payload["automationMode"] = automation
|
| 61 |
+
|
| 62 |
+
logger.info(f"Creating session with payload: {json.dumps(payload)}")
|
| 63 |
+
try:
|
| 64 |
+
return await self._request("POST", "/sessions", json=payload)
|
| 65 |
+
except Exception as e:
|
| 66 |
+
err_msg = str(e).lower()
|
| 67 |
+
if "maximum number of sessions" in err_msg or "max sessions reached" in err_msg or "400" in err_msg:
|
| 68 |
+
logger.warning(f"Possible quota/session limit error detected: {e}. Triggering early key rotation.")
|
| 69 |
+
force_rotate_key()
|
| 70 |
+
return {"error": f"Session creation failed: {e}. API key has been rotated. Please retry."}
|
| 71 |
+
|
| 72 |
+
logger.warning(f"REST API create_session failed: {e}. Falling back to CLI.")
|
| 73 |
+
cli_source = source.replace("sources/github/", "")
|
| 74 |
+
return await self.create_session_cli(prompt, cli_source, branch, title)
|
| 75 |
+
|
| 76 |
+
async def create_session_cli(self, prompt, source, branch="main", title="API Session"):
|
| 77 |
+
"""Fallback to analysis CLI for session creation."""
|
| 78 |
+
logger.info(f"Using analysis CLI to create session for {source}")
|
| 79 |
+
cmd = [
|
| 80 |
+
"analysis", "new",
|
| 81 |
+
"--repo", source,
|
| 82 |
+
prompt
|
| 83 |
+
]
|
| 84 |
+
env = os.environ.copy()
|
| 85 |
+
env["JULES_API_KEY"] = self.api_key
|
| 86 |
+
npm_bin = subprocess.check_output(["npm", "config", "get", "prefix"]).decode().strip() + "/bin"
|
| 87 |
+
env["PATH"] = f"{env.get('PATH', '')}:{npm_bin}"
|
| 88 |
+
|
| 89 |
+
try:
|
| 90 |
+
process = await asyncio.create_subprocess_exec(
|
| 91 |
+
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, env=env
|
| 92 |
+
)
|
| 93 |
+
stdout, stderr = await process.communicate()
|
| 94 |
+
output = stdout.decode().strip()
|
| 95 |
+
match = re.search(r'sessions/(\d+)', output)
|
| 96 |
+
if match:
|
| 97 |
+
s_id = match.group(0)
|
| 98 |
+
return {"name": s_id, "id": match.group(1)}
|
| 99 |
+
return {"output": output}
|
| 100 |
+
except:
|
| 101 |
+
return {"error": "CLI failed"}
|
| 102 |
+
|
| 103 |
+
async def get_session(self, session_id):
|
| 104 |
+
session_id = str(session_id).strip()
|
| 105 |
+
if not session_id.startswith("sessions/"): session_id = f"sessions/{session_id}"
|
| 106 |
+
return await self._request("GET", f"/{session_id}")
|
| 107 |
+
|
| 108 |
+
async def list_sessions(self, page_size=30):
|
| 109 |
+
return await self._request("GET", "/sessions", params={"pageSize": page_size})
|
| 110 |
+
|
| 111 |
+
async def send_message(self, session_id, prompt):
|
| 112 |
+
session_id = str(session_id).strip()
|
| 113 |
+
if not session_id.startswith("sessions/"): session_id = f"sessions/{session_id}"
|
| 114 |
+
return await self._request("POST", f"/{session_id}:sendMessage", json={"prompt": prompt})
|
| 115 |
+
|
| 116 |
+
async def approve_plan(self, session_id):
|
| 117 |
+
session_id = str(session_id).strip()
|
| 118 |
+
if not session_id.startswith("sessions/"): session_id = f"sessions/{session_id}"
|
| 119 |
+
return await self._request("POST", f"/{session_id}:approvePlan", json={})
|
| 120 |
+
|
| 121 |
+
async def get_activity(self, name: str):
|
| 122 |
+
name = str(name).strip()
|
| 123 |
+
return await self._request("GET", f"/{name}")
|
| 124 |
+
|
| 125 |
+
async def list_activities(self, session_id, page_size=50):
|
| 126 |
+
session_id = str(session_id).strip()
|
| 127 |
+
if not session_id.startswith("sessions/"): session_id = f"sessions/{session_id}"
|
| 128 |
+
return await self._request("GET", f"/{session_id}/activities", params={"pageSize": page_size})
|
| 129 |
+
|
| 130 |
+
# API Key Rotation State
|
| 131 |
+
REQUEST_COUNT = 0
|
| 132 |
+
CURRENT_KEY_INDEX = 0
|
| 133 |
+
|
| 134 |
+
def force_rotate_key():
|
| 135 |
+
"""Immediately switch to the next available API key and reset counter."""
|
| 136 |
+
global REQUEST_COUNT, CURRENT_KEY_INDEX
|
| 137 |
+
keys = [os.environ.get("JULES_API_KEY"), os.environ.get("JULES_API_KEY_FALLBACK")]
|
| 138 |
+
keys = [k for k in keys if k]
|
| 139 |
+
if len(keys) > 1:
|
| 140 |
+
CURRENT_KEY_INDEX = (CURRENT_KEY_INDEX + 1) % len(keys)
|
| 141 |
+
REQUEST_COUNT = 0
|
| 142 |
+
logger.info(f"FORCE ROTATION: Switched to Jules API Key index {CURRENT_KEY_INDEX}")
|
| 143 |
+
|
| 144 |
+
def get_rotated_api_key():
|
| 145 |
+
global REQUEST_COUNT, CURRENT_KEY_INDEX
|
| 146 |
+
keys = [os.environ.get("JULES_API_KEY"), os.environ.get("JULES_API_KEY_FALLBACK")]
|
| 147 |
+
# Filter out None or empty keys
|
| 148 |
+
keys = [k for k in keys if k]
|
| 149 |
+
if not keys:
|
| 150 |
+
logger.error("No Jules API keys found in environment.")
|
| 151 |
+
return None
|
| 152 |
+
|
| 153 |
+
key = keys[CURRENT_KEY_INDEX]
|
| 154 |
+
|
| 155 |
+
REQUEST_COUNT += 1
|
| 156 |
+
if REQUEST_COUNT >= 3:
|
| 157 |
+
REQUEST_COUNT = 0
|
| 158 |
+
old_index = CURRENT_KEY_INDEX
|
| 159 |
+
CURRENT_KEY_INDEX = (CURRENT_KEY_INDEX + 1) % len(keys)
|
| 160 |
+
if len(keys) > 1 and old_index != CURRENT_KEY_INDEX:
|
| 161 |
+
logger.info(f"Requests per key reached. Rotating Jules API Key from index {old_index} to {CURRENT_KEY_INDEX}")
|
| 162 |
+
|
| 163 |
+
return key
|
| 164 |
+
|
| 165 |
+
def get_client():
|
| 166 |
+
key = get_rotated_api_key()
|
| 167 |
+
return AnalysisClient(key)
|
| 168 |
+
|
| 169 |
+
@mcp.tool()
|
| 170 |
+
async def list_sources():
|
| 171 |
+
"""List all available sources (repositories)."""
|
| 172 |
+
return await get_client().list_sources()
|
| 173 |
+
|
| 174 |
+
@mcp.tool()
|
| 175 |
+
async def get_source(name: str):
|
| 176 |
+
"""
|
| 177 |
+
Get details about a specific source.
|
| 178 |
+
:param name: The source name (e.g., 'sources/github/owner/repo').
|
| 179 |
+
"""
|
| 180 |
+
return await get_client().get_source(name)
|
| 181 |
+
|
| 182 |
+
@mcp.tool()
|
| 183 |
+
async def create_session(
|
| 184 |
+
prompt: str,
|
| 185 |
+
source: str,
|
| 186 |
+
branch: str = "main",
|
| 187 |
+
title: str = "API Session",
|
| 188 |
+
automation_mode: str = "AUTO_CREATE_PR"
|
| 189 |
+
):
|
| 190 |
+
"""
|
| 191 |
+
Initialize a new autonomous coding session for a repository.
|
| 192 |
+
|
| 193 |
+
Args:
|
| 194 |
+
prompt: The implementation task or instructions for Jules.
|
| 195 |
+
source: Source repository path (e.g., 'sources/github/owner/repo').
|
| 196 |
+
branch: The branch to use.
|
| 197 |
+
title: Title for the session.
|
| 198 |
+
automation_mode: Defaults to 'AUTO_CREATE_PR'.
|
| 199 |
+
"""
|
| 200 |
+
return await get_client().create_session(prompt, source, branch, title, automation_mode)
|
| 201 |
+
|
| 202 |
+
@mcp.tool()
|
| 203 |
+
async def get_session(session_id: str):
|
| 204 |
+
"""
|
| 205 |
+
Get the current status and details of a session.
|
| 206 |
+
:param session_id: The session ID (e.g., 'sessions/123').
|
| 207 |
+
"""
|
| 208 |
+
return await get_client().get_session(session_id)
|
| 209 |
+
|
| 210 |
+
@mcp.tool()
|
| 211 |
+
async def list_sessions(page_size: int = 30):
|
| 212 |
+
"""List recent Jules sessions."""
|
| 213 |
+
return await get_client().list_sessions(page_size)
|
| 214 |
+
|
| 215 |
+
@mcp.tool()
|
| 216 |
+
async def send_message(session_id: str, prompt: str):
|
| 217 |
+
"""
|
| 218 |
+
Send a message or feedback to an existing session.
|
| 219 |
+
:param session_id: The session ID (e.g., 'sessions/123').
|
| 220 |
+
:param prompt: The message/instruction text.
|
| 221 |
+
"""
|
| 222 |
+
return await get_client().send_message(session_id, prompt)
|
| 223 |
+
|
| 224 |
+
@mcp.tool()
|
| 225 |
+
async def approve_plan(session_id: str):
|
| 226 |
+
"""
|
| 227 |
+
Approve the implementation plan for a session.
|
| 228 |
+
:param session_id: The session ID.
|
| 229 |
+
"""
|
| 230 |
+
return await get_client().approve_plan(session_id)
|
| 231 |
+
|
| 232 |
+
@mcp.tool()
|
| 233 |
+
async def get_activity(name: str):
|
| 234 |
+
"""
|
| 235 |
+
Get details about a specific activity.
|
| 236 |
+
:param name: Activity resource name (e.g., 'sessions/123/activities/456').
|
| 237 |
+
"""
|
| 238 |
+
return await get_client().get_activity(name)
|
| 239 |
+
|
| 240 |
+
@mcp.tool()
|
| 241 |
+
async def list_activities(session_id: str, page_size: int = 50):
|
| 242 |
+
"""
|
| 243 |
+
List activities for a specific session.
|
| 244 |
+
:param session_id: The session ID.
|
| 245 |
+
:param page_size: Number of activities to return.
|
| 246 |
+
"""
|
| 247 |
+
return await get_client().list_activities(session_id, page_size)
|
| 248 |
+
|
| 249 |
+
if __name__ == "__main__": mcp.run()
|
mcp/servers.txt
CHANGED
|
@@ -765,16 +765,16 @@ Hugging Face logo
|
|
| 765 |
Hugging Face
|
| 766 |
|
| 767 |
|
| 768 |
-
|
| 769 |
|
| 770 |
Authentication
|
| 771 |
|
| 772 |
-
To get started with the
|
| 773 |
Generate Your API Key
|
| 774 |
|
| 775 |
-
In the
|
| 776 |
|
| 777 |
-
|
| 778 |
Use Your API Key
|
| 779 |
|
| 780 |
To authenticate your requests, pass the API key in the X-Goog-Api-Key header of your API calls.
|
|
@@ -782,19 +782,19 @@ To authenticate your requests, pass the API key in the X-Goog-Api-Key header of
|
|
| 782 |
Keep your API keys secure. Don’t share them or embed them in public code. For your protection, any API keys found to be publicly exposed will be automatically disabled to prevent abuse.
|
| 783 |
API concepts
|
| 784 |
|
| 785 |
-
The
|
| 786 |
|
| 787 |
-
Source — An input source for the agent (e.g., a GitHub repository). Before using a source using the API, you must first install the
|
| 788 |
Session — A continuous unit of work within a specific context, similar to a chat session. A session is initiated with a prompt and a source.
|
| 789 |
Activity — A single unit of work within a Session. A Session contains multiple activities from both the user and the agent, such as generating a plan, sending a message, or updating progress.
|
| 790 |
|
| 791 |
Your first API call
|
| 792 |
|
| 793 |
-
We’ll walk through creating your first session with the
|
| 794 |
|
| 795 |
List your available sources
|
| 796 |
|
| 797 |
-
First, you need to find the name of the source you want to work with (e.g., your GitHub repo). This command will return a list of all sources you have connected to
|
| 798 |
|
| 799 |
curl -H "x-goog-api-key: $JULES_API_KEY" \
|
| 800 |
https://jules.googleapis.com/v1alpha/sources
|
|
@@ -817,7 +817,7 @@ The response will look something like this:
|
|
| 817 |
|
| 818 |
Create a new session
|
| 819 |
|
| 820 |
-
Now, create a new session. You’ll need the source name from the previous step. This request tells
|
| 821 |
|
| 822 |
curl 'https://jules.googleapis.com/v1alpha/sessions' \
|
| 823 |
-X POST \
|
|
@@ -915,7 +915,7 @@ The response will be empty because the agent will send its response in the next
|
|
| 915 |
|
| 916 |
Sessions
|
| 917 |
|
| 918 |
-
Sessions are the core resource in the
|
| 919 |
Create a Session
|
| 920 |
POST
|
| 921 |
/v1alpha/sessions
|
|
@@ -925,7 +925,7 @@ Request Body
|
|
| 925 |
prompt
|
| 926 |
required string
|
| 927 |
|
| 928 |
-
The task description for
|
| 929 |
title
|
| 930 |
string
|
| 931 |
|
|
@@ -1078,7 +1078,7 @@ POST
|
|
| 1078 |
|
| 1079 |
Sends a message from the user to an active session.
|
| 1080 |
|
| 1081 |
-
Use this endpoint to provide feedback, answer questions, or give additional instructions to
|
| 1082 |
Path Parameters
|
| 1083 |
session
|
| 1084 |
required string path
|
|
@@ -1134,10 +1134,10 @@ Session States
|
|
| 1134 |
Sessions progress through the following states:
|
| 1135 |
State Description
|
| 1136 |
QUEUED Session is waiting to be processed
|
| 1137 |
-
PLANNING
|
| 1138 |
AWAITING_PLAN_APPROVAL Plan is ready and waiting for user approval
|
| 1139 |
-
AWAITING_USER_FEEDBACK
|
| 1140 |
-
IN_PROGRESS
|
| 1141 |
PAUSED Session is paused
|
| 1142 |
COMPLETED Task completed successfully
|
| 1143 |
FAILED Task failed to complete
|
|
@@ -1262,7 +1262,7 @@ Activity Types
|
|
| 1262 |
Activities have different types based on what occurred. Each activity will have exactly one of these event fields populated:
|
| 1263 |
Plan Generated
|
| 1264 |
|
| 1265 |
-
Indicates
|
| 1266 |
|
| 1267 |
{
|
| 1268 |
"planGenerated": {
|
|
@@ -1298,7 +1298,7 @@ A message from the user:
|
|
| 1298 |
|
| 1299 |
Agent Messaged
|
| 1300 |
|
| 1301 |
-
A message from
|
| 1302 |
|
| 1303 |
{
|
| 1304 |
"agentMessaged": {
|
|
@@ -1384,9 +1384,9 @@ Media
|
|
| 1384 |
|
| 1385 |
Sources
|
| 1386 |
|
| 1387 |
-
Sources represent repositories connected to
|
| 1388 |
|
| 1389 |
-
Sources are created when you connect a GitHub repository to
|
| 1390 |
List Sources
|
| 1391 |
GET
|
| 1392 |
/v1alpha/sources
|
|
@@ -1527,11 +1527,11 @@ Next
|
|
| 1527 |
Types
|
| 1528 |
|
| 1529 |
|
| 1530 |
-
This page documents all data types used in the
|
| 1531 |
Core Resources
|
| 1532 |
Session
|
| 1533 |
|
| 1534 |
-
A session represents a unit of work where
|
| 1535 |
Session
|
| 1536 |
|
| 1537 |
A session is a contiguous amount of work within the same context.
|
|
@@ -1546,7 +1546,7 @@ Output only. The session ID.
|
|
| 1546 |
prompt
|
| 1547 |
required string
|
| 1548 |
|
| 1549 |
-
The task description for
|
| 1550 |
title
|
| 1551 |
string
|
| 1552 |
|
|
@@ -1558,7 +1558,7 @@ Output only. Current state of the session.
|
|
| 1558 |
url
|
| 1559 |
string
|
| 1560 |
|
| 1561 |
-
Output only. URL to view the session in the
|
| 1562 |
sourceContext
|
| 1563 |
required SourceContext
|
| 1564 |
|
|
@@ -1589,10 +1589,10 @@ Enum representing the current state of a session:
|
|
| 1589 |
Value Description
|
| 1590 |
STATE_UNSPECIFIED State is unspecified
|
| 1591 |
QUEUED Session is waiting to be processed
|
| 1592 |
-
PLANNING
|
| 1593 |
AWAITING_PLAN_APPROVAL Plan is ready for user approval
|
| 1594 |
-
AWAITING_USER_FEEDBACK
|
| 1595 |
-
IN_PROGRESS
|
| 1596 |
PAUSED Session is paused
|
| 1597 |
FAILED Session failed
|
| 1598 |
COMPLETED Session completed successfully
|
|
@@ -1647,7 +1647,7 @@ The user posted a message.
|
|
| 1647 |
agentMessaged
|
| 1648 |
AgentMessaged
|
| 1649 |
|
| 1650 |
-
|
| 1651 |
progressUpdated
|
| 1652 |
ProgressUpdated
|
| 1653 |
|
|
@@ -1682,7 +1682,7 @@ Plans
|
|
| 1682 |
Plan
|
| 1683 |
Plan
|
| 1684 |
|
| 1685 |
-
A sequence of steps that
|
| 1686 |
id
|
| 1687 |
string
|
| 1688 |
|
|
@@ -1895,7 +1895,7 @@ The message content.
|
|
| 1895 |
AgentMessaged
|
| 1896 |
AgentMessaged
|
| 1897 |
|
| 1898 |
-
|
| 1899 |
agentMessage
|
| 1900 |
string
|
| 1901 |
|
|
|
|
| 765 |
Hugging Face
|
| 766 |
|
| 767 |
|
| 768 |
+
Analysis API
|
| 769 |
|
| 770 |
Authentication
|
| 771 |
|
| 772 |
+
To get started with the Analysis REST API, you’ll need an API key.
|
| 773 |
Generate Your API Key
|
| 774 |
|
| 775 |
+
In the Analysis web app, go to the Settings page to create a new API key. You can have at most 3 API keys at a time.
|
| 776 |
|
| 777 |
+
Analysis REST API Key creation interface
|
| 778 |
Use Your API Key
|
| 779 |
|
| 780 |
To authenticate your requests, pass the API key in the X-Goog-Api-Key header of your API calls.
|
|
|
|
| 782 |
Keep your API keys secure. Don’t share them or embed them in public code. For your protection, any API keys found to be publicly exposed will be automatically disabled to prevent abuse.
|
| 783 |
API concepts
|
| 784 |
|
| 785 |
+
The Analysis REST API is built around a few core resources. Understanding these will help you use the API effectively.
|
| 786 |
|
| 787 |
+
Source — An input source for the agent (e.g., a GitHub repository). Before using a source using the API, you must first install the Analysis GitHub app through the Analysis web app.
|
| 788 |
Session — A continuous unit of work within a specific context, similar to a chat session. A session is initiated with a prompt and a source.
|
| 789 |
Activity — A single unit of work within a Session. A Session contains multiple activities from both the user and the agent, such as generating a plan, sending a message, or updating progress.
|
| 790 |
|
| 791 |
Your first API call
|
| 792 |
|
| 793 |
+
We’ll walk through creating your first session with the Analysis REST API using curl.
|
| 794 |
|
| 795 |
List your available sources
|
| 796 |
|
| 797 |
+
First, you need to find the name of the source you want to work with (e.g., your GitHub repo). This command will return a list of all sources you have connected to Analysis.
|
| 798 |
|
| 799 |
curl -H "x-goog-api-key: $JULES_API_KEY" \
|
| 800 |
https://jules.googleapis.com/v1alpha/sources
|
|
|
|
| 817 |
|
| 818 |
Create a new session
|
| 819 |
|
| 820 |
+
Now, create a new session. You’ll need the source name from the previous step. This request tells Analysis to create a boba app in the specified repository.
|
| 821 |
|
| 822 |
curl 'https://jules.googleapis.com/v1alpha/sessions' \
|
| 823 |
-X POST \
|
|
|
|
| 915 |
|
| 916 |
Sessions
|
| 917 |
|
| 918 |
+
Sessions are the core resource in the Analysis REST API. A session represents a unit of work where Analysis executes a coding task on your repository.
|
| 919 |
Create a Session
|
| 920 |
POST
|
| 921 |
/v1alpha/sessions
|
|
|
|
| 925 |
prompt
|
| 926 |
required string
|
| 927 |
|
| 928 |
+
The task description for Analysis to execute.
|
| 929 |
title
|
| 930 |
string
|
| 931 |
|
|
|
|
| 1078 |
|
| 1079 |
Sends a message from the user to an active session.
|
| 1080 |
|
| 1081 |
+
Use this endpoint to provide feedback, answer questions, or give additional instructions to Analysis during an active session.
|
| 1082 |
Path Parameters
|
| 1083 |
session
|
| 1084 |
required string path
|
|
|
|
| 1134 |
Sessions progress through the following states:
|
| 1135 |
State Description
|
| 1136 |
QUEUED Session is waiting to be processed
|
| 1137 |
+
PLANNING Analysis is analyzing the task and creating a plan
|
| 1138 |
AWAITING_PLAN_APPROVAL Plan is ready and waiting for user approval
|
| 1139 |
+
AWAITING_USER_FEEDBACK Analysis needs additional input from the user
|
| 1140 |
+
IN_PROGRESS Analysis is actively working on the task
|
| 1141 |
PAUSED Session is paused
|
| 1142 |
COMPLETED Task completed successfully
|
| 1143 |
FAILED Task failed to complete
|
|
|
|
| 1262 |
Activities have different types based on what occurred. Each activity will have exactly one of these event fields populated:
|
| 1263 |
Plan Generated
|
| 1264 |
|
| 1265 |
+
Indicates Analysis has created a plan for the task:
|
| 1266 |
|
| 1267 |
{
|
| 1268 |
"planGenerated": {
|
|
|
|
| 1298 |
|
| 1299 |
Agent Messaged
|
| 1300 |
|
| 1301 |
+
A message from Analysis:
|
| 1302 |
|
| 1303 |
{
|
| 1304 |
"agentMessaged": {
|
|
|
|
| 1384 |
|
| 1385 |
Sources
|
| 1386 |
|
| 1387 |
+
Sources represent repositories connected to Analysis. Currently, Analysis supports GitHub repositories. Use the Sources API to list available repositories and get details about specific sources.
|
| 1388 |
|
| 1389 |
+
Sources are created when you connect a GitHub repository to Analysis through the web interface. The API currently only supports reading sources, not creating them.
|
| 1390 |
List Sources
|
| 1391 |
GET
|
| 1392 |
/v1alpha/sources
|
|
|
|
| 1527 |
Types
|
| 1528 |
|
| 1529 |
|
| 1530 |
+
This page documents all data types used in the Analysis REST API.
|
| 1531 |
Core Resources
|
| 1532 |
Session
|
| 1533 |
|
| 1534 |
+
A session represents a unit of work where Analysis executes a coding task.
|
| 1535 |
Session
|
| 1536 |
|
| 1537 |
A session is a contiguous amount of work within the same context.
|
|
|
|
| 1546 |
prompt
|
| 1547 |
required string
|
| 1548 |
|
| 1549 |
+
The task description for Analysis to execute.
|
| 1550 |
title
|
| 1551 |
string
|
| 1552 |
|
|
|
|
| 1558 |
url
|
| 1559 |
string
|
| 1560 |
|
| 1561 |
+
Output only. URL to view the session in the Analysis web app.
|
| 1562 |
sourceContext
|
| 1563 |
required SourceContext
|
| 1564 |
|
|
|
|
| 1589 |
Value Description
|
| 1590 |
STATE_UNSPECIFIED State is unspecified
|
| 1591 |
QUEUED Session is waiting to be processed
|
| 1592 |
+
PLANNING Analysis is creating a plan
|
| 1593 |
AWAITING_PLAN_APPROVAL Plan is ready for user approval
|
| 1594 |
+
AWAITING_USER_FEEDBACK Analysis needs user input
|
| 1595 |
+
IN_PROGRESS Analysis is actively working
|
| 1596 |
PAUSED Session is paused
|
| 1597 |
FAILED Session failed
|
| 1598 |
COMPLETED Session completed successfully
|
|
|
|
| 1647 |
agentMessaged
|
| 1648 |
AgentMessaged
|
| 1649 |
|
| 1650 |
+
Analysis posted a message.
|
| 1651 |
progressUpdated
|
| 1652 |
ProgressUpdated
|
| 1653 |
|
|
|
|
| 1682 |
Plan
|
| 1683 |
Plan
|
| 1684 |
|
| 1685 |
+
A sequence of steps that Analysis will take to complete the task.
|
| 1686 |
id
|
| 1687 |
string
|
| 1688 |
|
|
|
|
| 1895 |
AgentMessaged
|
| 1896 |
AgentMessaged
|
| 1897 |
|
| 1898 |
+
Analysis posted a message.
|
| 1899 |
agentMessage
|
| 1900 |
string
|
| 1901 |
|