mcp-server-cloud-agent 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MetalTorque
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,28 +1,43 @@
1
1
  # mcp-server-cloud-agent
2
2
 
3
- MCP server for **Cloud Agent** — an AI software engineer that writes code, opens PRs, reviews code, generates tests, runs security scans, and answers codebase questions.
3
+ MCP server for **Cloud Agent** — a hosted AI software engineer that writes code, opens PRs, reviews code, generates tests, runs security scans, and answers codebase questions.
4
4
 
5
5
  Connect from any MCP client (Claude Code, Cursor, Windsurf, or your own agents) and delegate engineering tasks.
6
6
 
7
+ ## How it works
8
+
9
+ This package is a **local stdio MCP proxy** that forwards requests to the Cloud Agent hosted backend at `agent.leddconsulting.com`. Your MCP client communicates with this server over stdio; the server makes authenticated HTTPS calls to the backend on your behalf.
10
+
11
+ **Data flow:** MCP client → (stdio) → this server → (HTTPS) → Cloud Agent backend → GitHub
12
+
13
+ **What data leaves your machine:**
14
+ - Task descriptions, repo names, file paths, and PR URLs you provide
15
+ - Your API key (sent over HTTPS only, never over HTTP)
16
+
17
+ **What the backend does with your data:**
18
+ - Clones repos from GitHub using its own GitHub App credentials
19
+ - Executes tasks in isolated sandboxes
20
+ - Opens PRs and posts reviews to GitHub on your behalf
21
+
7
22
  ## Tools (9)
8
23
 
9
- | Tool | Description |
10
- |------|-------------|
11
- | `run_task` | Write code, fix bugs, add features — returns result + PR URL |
12
- | `review_pr` | Review a GitHub PR with structured feedback, optionally post to GitHub |
13
- | `ask_codebase` | Ask questions about any GitHub repo (auto-indexes on first use) |
14
- | `generate_tests` | Generate tests for a file or feature, opens a PR |
15
- | `security_scan` | Security + dependency scan across one or more repos |
16
- | `list_sessions` | List recent sessions with status, cost, duration, PR URLs |
17
- | `list_playbooks` | List available workflow templates (bug-triage, test-coverage, etc.) |
18
- | `run_playbook` | Run a playbook against a repo |
19
- | `get_usage` | Usage stats — sessions, cost, time saved, breakdowns |
24
+ | Tool | Description | Side effects |
25
+ |------|-------------|--------------|
26
+ | `run_task` | Write code, fix bugs, add features — returns result + PR URL | Creates branches and PRs |
27
+ | `review_pr` | Review a GitHub PR with structured feedback | Optionally posts comments to GitHub |
28
+ | `ask_codebase` | Ask questions about any GitHub repo (auto-indexes on first use) | Read-only |
29
+ | `generate_tests` | Generate tests for a file, opens a PR | Creates branches and PRs |
30
+ | `security_scan` | Security + dependency scan across one or more repos | Read-only |
31
+ | `list_sessions` | List recent sessions with status, cost, duration, PR URLs | Read-only |
32
+ | `list_playbooks` | List available workflow templates | Read-only |
33
+ | `run_playbook` | Run a playbook against a repo | Creates branches and PRs |
34
+ | `get_usage` | Usage stats — sessions, cost, time saved, breakdowns | Read-only |
20
35
 
21
36
  ## Setup
22
37
 
23
38
  ### 1. Get an API key
24
39
 
25
- Sign in to your Cloud Agent web workspace and generate an API key at `/auth/api-key`.
40
+ Sign in to your Cloud Agent workspace at [agent.leddconsulting.com](https://agent.leddconsulting.com) and generate an API key at `/auth/api-key`. Keys use the `ca_*` prefix.
26
41
 
27
42
  ### 2. Configure your MCP client
28
43
 
@@ -61,35 +76,58 @@ Sign in to your Cloud Agent web workspace and generate an API key at `/auth/api-
61
76
  | Variable | Required | Description |
62
77
  |----------|----------|-------------|
63
78
  | `CLOUD_AGENT_API_KEY` | Yes | API key (`ca_*` prefix) from your Cloud Agent workspace |
64
- | `CLOUD_AGENT_URL` | No | Backend URL (defaults to `https://cloudagent.metaltorque.dev`) |
79
+ | `CLOUD_AGENT_URL` | No | Backend URL (defaults to `https://agent.leddconsulting.com`) |
65
80
 
66
81
  ## Usage Examples
67
82
 
68
83
  Once configured, your MCP client can call these tools directly:
69
84
 
70
85
  **Fix a bug:**
71
- > "Use cloud-agent to fix the broken login flow in myorg/myapp"
86
+ > "Use cloud-agent run_task on myorg/myapp to fix the broken login flow"
72
87
 
73
88
  **Review a PR:**
74
- > "Use cloud-agent to review https://github.com/myorg/myapp/pull/42"
89
+ > "Use cloud-agent review_pr on https://github.com/myorg/myapp/pull/42"
75
90
 
76
91
  **Ask about code:**
77
- > "Use cloud-agent to explain how authentication works in myorg/myapp"
92
+ > "Use cloud-agent ask_codebase on myorg/myapp: how does authentication work?"
78
93
 
79
94
  **Generate tests:**
80
- > "Use cloud-agent to generate tests for src/auth.ts in myorg/myapp"
95
+ > "Use cloud-agent generate_tests on myorg/myapp for src/auth.ts"
81
96
 
82
97
  **Security scan:**
83
- > "Use cloud-agent to scan myorg/myapp and myorg/api for vulnerabilities"
98
+ > "Use cloud-agent security_scan on myorg/myapp and myorg/api"
99
+
100
+ ## Sample Output
84
101
 
85
- **Run a playbook:**
86
- > "Use cloud-agent to run the bug-triage playbook on myorg/myapp"
102
+ **run_task response:**
103
+ ```json
104
+ {
105
+ "response": "Fixed the login redirect bug. Changed src/auth.ts to properly handle OAuth callback URLs.",
106
+ "cost_usd": 0.42,
107
+ "duration_ms": 45000,
108
+ "pr_url": "https://github.com/myorg/myapp/pull/87"
109
+ }
110
+ ```
87
111
 
88
- ## What is Cloud Agent?
112
+ **security_scan response:**
113
+ ```json
114
+ {
115
+ "repos_scanned": 1,
116
+ "vulnerabilities": 3,
117
+ "secrets_found": 0,
118
+ "findings": [...]
119
+ }
120
+ ```
89
121
 
90
- Cloud Agent is a Devin-alternative that puts an AI software engineer where your team already works — Slack, Teams, Jira, Linear, and 12 more platforms. It writes code, opens PRs, reviews code, generates tests, runs security scans, and answers codebase questions.
122
+ ## Troubleshooting
91
123
 
92
- This MCP server gives any MCP-compatible AI client the same capabilities.
124
+ | Problem | Solution |
125
+ |---------|----------|
126
+ | "CLOUD_AGENT_API_KEY is required" | Set the env var in your MCP client config |
127
+ | "Refusing to send API key over insecure HTTP" | Use HTTPS (the default). Don't set `CLOUD_AGENT_URL` to an HTTP URL |
128
+ | "Request timed out" | Tasks can take up to 10 minutes. Check `list_sessions` for status |
129
+ | "HTTP 401" | Your API key is invalid or expired. Generate a new one |
130
+ | "HTTP 429" | Rate limited. Wait and retry |
93
131
 
94
132
  ## License
95
133
 
package/glama.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
3
+ "maintainers": ["joepangallo"]
4
+ }
package/index.js CHANGED
@@ -10,7 +10,12 @@ const { version } = require("./package.json");
10
10
  // ── Config ──────────────────────────────────────────────────────────
11
11
 
12
12
  const API_KEY = process.env.CLOUD_AGENT_API_KEY || "";
13
- const BASE_URL = (process.env.CLOUD_AGENT_URL || "https://cloudagent.metaltorque.dev").replace(/\/$/, "");
13
+ const BASE_URL = (process.env.CLOUD_AGENT_URL || "https://agent.leddconsulting.com").replace(/\/$/, "");
14
+
15
+ // ── Shared schemas ──────────────────────────────────────────────────
16
+
17
+ const repoSchema = z.string().regex(/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/, "Must be owner/repo format, e.g. 'facebook/react'");
18
+ const prUrlSchema = z.string().url().regex(/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/, "Must be a GitHub PR URL, e.g. https://github.com/owner/repo/pull/123");
14
19
 
15
20
  // ── HTTP helper ─────────────────────────────────────────────────────
16
21
 
@@ -70,8 +75,11 @@ function request(method, urlPath, body, timeout = 600_000) {
70
75
  });
71
76
  }
72
77
 
78
+ // ── Shared helpers ──────────────────────────────────────────────────
79
+
73
80
  function noKeyError() {
74
81
  return {
82
+ isError: true,
75
83
  content: [{
76
84
  type: "text",
77
85
  text: "Error: CLOUD_AGENT_API_KEY environment variable is required.\n\nGet an API key from your Cloud Agent web workspace at /auth/api-key.\nAPI keys use the ca_* prefix.",
@@ -79,6 +87,29 @@ function noKeyError() {
79
87
  };
80
88
  }
81
89
 
90
+ function errorResult(e) {
91
+ return {
92
+ isError: true,
93
+ content: [{ type: "text", text: `Error: ${e.message}` }],
94
+ };
95
+ }
96
+
97
+ function toolResult(text) {
98
+ return {
99
+ content: [{ type: "text", text }],
100
+ };
101
+ }
102
+
103
+ async function authedCall(method, path, body, formatter) {
104
+ if (!API_KEY) return noKeyError();
105
+ try {
106
+ const result = await request(method, path, body);
107
+ return toolResult(formatter(result));
108
+ } catch (e) {
109
+ return errorResult(e);
110
+ }
111
+ }
112
+
82
113
  // ── MCP Server ──────────────────────────────────────────────────────
83
114
 
84
115
  const server = new McpServer({
@@ -87,31 +118,29 @@ const server = new McpServer({
87
118
  });
88
119
 
89
120
  // ── Tool: run_task ──────────────────────────────────────────────────
121
+ // Positional: tool(name, description, paramsSchema, annotations, handler)
90
122
 
91
123
  server.tool(
92
124
  "run_task",
93
125
  "Run a coding task: write code, fix bugs, add features, refactor. The AI agent clones the repo, makes changes, and opens a PR. Returns the result and PR URL when complete.",
94
126
  {
95
- prompt: z.string().describe("Task description, e.g. 'Fix the login bug in owner/repo' or 'Add dark mode to owner/repo'"),
127
+ repo: repoSchema.describe("GitHub repo in owner/repo format, e.g. 'facebook/react'"),
128
+ task: z.string().min(1).describe("Task description, e.g. 'Fix the login bug' or 'Add dark mode to the settings page'"),
129
+ base_branch: z.string().optional().describe("Branch to base changes on (default: main)"),
96
130
  },
97
- async ({ prompt }) => {
98
- if (!API_KEY) return noKeyError();
99
- try {
100
- const result = await request("POST", "/query", { prompt });
101
- return {
102
- content: [{
103
- type: "text",
104
- text: JSON.stringify({
105
- response: result.response,
106
- cost_usd: result.cost_usd,
107
- duration_ms: result.duration_ms,
108
- pr_url: result.pr_url || null,
109
- }, null, 2),
110
- }],
111
- };
112
- } catch (e) {
113
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
114
- }
131
+ { destructiveHint: true, readOnlyHint: false, openWorldHint: true },
132
+ async ({ repo, task, base_branch }) => {
133
+ const prompt = base_branch
134
+ ? `In ${repo} (branch: ${base_branch}): ${task}`
135
+ : `In ${repo}: ${task}`;
136
+ return authedCall("POST", "/query", { prompt }, (result) =>
137
+ JSON.stringify({
138
+ response: result.response,
139
+ cost_usd: result.cost_usd,
140
+ duration_ms: result.duration_ms,
141
+ pr_url: result.pr_url || null,
142
+ }, null, 2)
143
+ );
115
144
  }
116
145
  );
117
146
 
@@ -121,26 +150,14 @@ server.tool(
121
150
  "review_pr",
122
151
  "Review a GitHub pull request. Returns structured feedback with issues, verdict, and suggestions. Optionally posts review comments directly to GitHub.",
123
152
  {
124
- pr_url: z.string().describe("Full GitHub PR URL, e.g. https://github.com/owner/repo/pull/123"),
153
+ pr_url: prUrlSchema.describe("Full GitHub PR URL, e.g. https://github.com/owner/repo/pull/123"),
125
154
  post_comments: z.boolean().optional().describe("Post review comments directly to GitHub (default: false)"),
126
155
  },
127
- async ({ pr_url, post_comments }) => {
128
- if (!API_KEY) return noKeyError();
129
- try {
130
- const result = await request("POST", "/review", {
131
- pr_url,
132
- post_review: post_comments === true,
133
- });
134
- return {
135
- content: [{
136
- type: "text",
137
- text: result.review || JSON.stringify(result, null, 2),
138
- }],
139
- };
140
- } catch (e) {
141
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
142
- }
143
- }
156
+ { destructiveHint: false, readOnlyHint: false, openWorldHint: true },
157
+ async ({ pr_url, post_comments }) =>
158
+ authedCall("POST", "/review", { pr_url, post_review: post_comments === true }, (result) =>
159
+ result.review || JSON.stringify(result, null, 2)
160
+ )
144
161
  );
145
162
 
146
163
  // ── Tool: ask_codebase ──────────────────────────────────────────────
@@ -149,58 +166,35 @@ server.tool(
149
166
  "ask_codebase",
150
167
  "Ask a question about any GitHub repository's codebase. Auto-indexes the repo on first use. Returns an answer with file references.",
151
168
  {
152
- question: z.string().describe("Question about the codebase, e.g. 'How does authentication work?'"),
153
- repo: z.string().describe("GitHub repo in owner/repo format, e.g. 'facebook/react'"),
169
+ question: z.string().min(1).describe("Question about the codebase, e.g. 'How does authentication work?'"),
170
+ repo: repoSchema.describe("GitHub repo in owner/repo format, e.g. 'facebook/react'"),
154
171
  },
155
- async ({ question, repo }) => {
156
- if (!API_KEY) return noKeyError();
157
- try {
158
- const result = await request("POST", "/ask", { question, repo });
159
- return {
160
- content: [{
161
- type: "text",
162
- text: result.answer || JSON.stringify(result, null, 2),
163
- }],
164
- };
165
- } catch (e) {
166
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
167
- }
168
- }
172
+ { destructiveHint: false, readOnlyHint: true, openWorldHint: true },
173
+ async ({ question, repo }) =>
174
+ authedCall("POST", "/ask", { question, repo }, (result) =>
175
+ result.answer || JSON.stringify(result, null, 2)
176
+ )
169
177
  );
170
178
 
171
179
  // ── Tool: generate_tests ────────────────────────────────────────────
172
180
 
173
181
  server.tool(
174
182
  "generate_tests",
175
- "Generate tests for a file or feature in a GitHub repository. Creates test files and opens a PR with them.",
183
+ "Generate tests for a specific file in a GitHub repository. Creates test files and opens a PR with them.",
176
184
  {
177
- repo: z.string().describe("GitHub repo in owner/repo format"),
178
- file: z.string().optional().describe("Specific file to test, e.g. 'src/auth.ts'"),
179
- feature: z.string().optional().describe("Feature to test, e.g. 'user authentication'"),
185
+ repo: repoSchema.describe("GitHub repo in owner/repo format"),
186
+ file: z.string().min(1).describe("File to generate tests for, e.g. 'src/auth.ts'"),
180
187
  },
181
- async ({ repo, file, feature }) => {
182
- if (!API_KEY) return noKeyError();
183
- const target = file || feature;
184
- if (!target) {
185
- return { content: [{ type: "text", text: "Error: Provide either 'file' or 'feature' to test." }] };
186
- }
187
- try {
188
- const result = await request("POST", "/test", { file, feature, repo });
189
- return {
190
- content: [{
191
- type: "text",
192
- text: JSON.stringify({
193
- response: result.response,
194
- cost_usd: result.cost_usd,
195
- duration_ms: result.duration_ms,
196
- pr_url: result.pr_url || null,
197
- }, null, 2),
198
- }],
199
- };
200
- } catch (e) {
201
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
202
- }
203
- }
188
+ { destructiveHint: true, readOnlyHint: false, openWorldHint: true },
189
+ async ({ repo, file }) =>
190
+ authedCall("POST", "/test", { file, repo }, (result) =>
191
+ JSON.stringify({
192
+ response: result.response,
193
+ cost_usd: result.cost_usd,
194
+ duration_ms: result.duration_ms,
195
+ pr_url: result.pr_url || null,
196
+ }, null, 2)
197
+ )
204
198
  );
205
199
 
206
200
  // ── Tool: security_scan ─────────────────────────────────────────────
@@ -209,23 +203,14 @@ server.tool(
209
203
  "security_scan",
210
204
  "Run a security and dependency scan on one or more GitHub repositories. Checks for vulnerabilities, secret exposure, and security anti-patterns.",
211
205
  {
212
- repos: z.array(z.string()).describe("Array of repos in owner/repo format, e.g. ['owner/repo1', 'owner/repo2']"),
206
+ repos: z.array(repoSchema).min(1).describe("Array of repos in owner/repo format, e.g. ['owner/repo1', 'owner/repo2']"),
213
207
  type: z.enum(["all", "dependencies", "secrets", "code"]).optional().describe("Scan type (default: all)"),
214
208
  },
215
- async ({ repos, type }) => {
216
- if (!API_KEY) return noKeyError();
217
- try {
218
- const result = await request("POST", "/scan", { repos, type: type || "all" });
219
- return {
220
- content: [{
221
- type: "text",
222
- text: JSON.stringify(result, null, 2),
223
- }],
224
- };
225
- } catch (e) {
226
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
227
- }
228
- }
209
+ { destructiveHint: false, readOnlyHint: true, openWorldHint: true },
210
+ async ({ repos, type }) =>
211
+ authedCall("POST", "/scan", { repos, type: type || "all" }, (result) =>
212
+ JSON.stringify(result, null, 2)
213
+ )
229
214
  );
230
215
 
231
216
  // ── Tool: list_sessions ─────────────────────────────────────────────
@@ -237,21 +222,13 @@ server.tool(
237
222
  limit: z.number().int().min(1).max(100).optional().describe("Max sessions to return (default: 20)"),
238
223
  status: z.enum(["running", "completed", "error"]).optional().describe("Filter by session status"),
239
224
  },
225
+ { destructiveHint: false, readOnlyHint: true, openWorldHint: false },
240
226
  async ({ limit, status }) => {
241
- if (!API_KEY) return noKeyError();
242
- try {
243
- let path = `/api/sessions?limit=${limit || 20}`;
244
- if (status) path += `&status=${status}`;
245
- const result = await request("GET", path);
246
- return {
247
- content: [{
248
- type: "text",
249
- text: JSON.stringify(result.sessions || result, null, 2),
250
- }],
251
- };
252
- } catch (e) {
253
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
254
- }
227
+ let path = `/api/sessions?limit=${limit || 20}`;
228
+ if (status) path += `&status=${status}`;
229
+ return authedCall("GET", path, undefined, (result) =>
230
+ JSON.stringify(result.sessions || result, null, 2)
231
+ );
255
232
  }
256
233
  );
257
234
 
@@ -261,20 +238,10 @@ server.tool(
261
238
  "list_playbooks",
262
239
  "List available playbooks — reusable workflow templates for common engineering tasks like bug triage, security remediation, test coverage, docs sync, and more.",
263
240
  {},
264
- async () => {
265
- if (!API_KEY) return noKeyError();
266
- try {
267
- const result = await request("GET", "/api/playbooks");
268
- return {
269
- content: [{
270
- type: "text",
271
- text: JSON.stringify(result, null, 2),
272
- }],
273
- };
274
- } catch (e) {
275
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
276
- }
277
- }
241
+ { destructiveHint: false, readOnlyHint: true, openWorldHint: false },
242
+ async () => authedCall("GET", "/api/playbooks", undefined, (result) =>
243
+ JSON.stringify(result, null, 2)
244
+ )
278
245
  );
279
246
 
280
247
  // ── Tool: run_playbook ──────────────────────────────────────────────
@@ -283,24 +250,15 @@ server.tool(
283
250
  "run_playbook",
284
251
  "Run a playbook (reusable workflow template) against a repository. Use list_playbooks to see available options. Built-in playbooks include: bug-triage, security-remediation, dependency-upgrade, docs-sync, test-coverage, code-migration, pr-review-cycle.",
285
252
  {
286
- slug: z.string().describe("Playbook slug, e.g. 'bug-triage', 'security-remediation', 'test-coverage'"),
287
- repo: z.string().describe("GitHub repo in owner/repo format"),
253
+ slug: z.string().min(1).describe("Playbook slug, e.g. 'bug-triage', 'security-remediation', 'test-coverage'"),
254
+ repo: repoSchema.describe("GitHub repo in owner/repo format"),
288
255
  inputs: z.record(z.string()).optional().describe("Additional inputs for the playbook template variables"),
289
256
  },
290
- async ({ slug, repo, inputs }) => {
291
- if (!API_KEY) return noKeyError();
292
- try {
293
- const result = await request("POST", `/api/playbooks/${encodeURIComponent(slug)}/run`, { repo, inputs });
294
- return {
295
- content: [{
296
- type: "text",
297
- text: JSON.stringify(result, null, 2),
298
- }],
299
- };
300
- } catch (e) {
301
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
302
- }
303
- }
257
+ { destructiveHint: true, readOnlyHint: false, openWorldHint: true },
258
+ async ({ slug, repo, inputs }) =>
259
+ authedCall("POST", `/api/playbooks/${encodeURIComponent(slug)}/run`, { repo, inputs }, (result) =>
260
+ JSON.stringify(result, null, 2)
261
+ )
304
262
  );
305
263
 
306
264
  // ── Tool: get_usage ─────────────────────────────────────────────────
@@ -311,20 +269,12 @@ server.tool(
311
269
  {
312
270
  days: z.number().int().min(1).max(365).optional().describe("Number of days to look back (default: all time)"),
313
271
  },
272
+ { destructiveHint: false, readOnlyHint: true, openWorldHint: false },
314
273
  async ({ days }) => {
315
- if (!API_KEY) return noKeyError();
316
- try {
317
- const path = days ? `/api/usage?days=${days}` : "/api/usage";
318
- const result = await request("GET", path);
319
- return {
320
- content: [{
321
- type: "text",
322
- text: JSON.stringify(result, null, 2),
323
- }],
324
- };
325
- } catch (e) {
326
- return { content: [{ type: "text", text: `Error: ${e.message}` }] };
327
- }
274
+ const path = days ? `/api/usage?days=${days}` : "/api/usage";
275
+ return authedCall("GET", path, undefined, (result) =>
276
+ JSON.stringify(result, null, 2)
277
+ );
328
278
  }
329
279
  );
330
280
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-cloud-agent",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "mcpName": "io.github.joepangallo/cloud-agent",
5
5
  "description": "MCP server for Cloud Agent — an AI software engineer that writes code, opens PRs, reviews code, generates tests, runs security scans, and answers codebase questions. Connect from any MCP client (Claude Code, Cursor, Windsurf) and delegate engineering tasks.",
6
6
  "bin": {
@@ -43,16 +43,27 @@
43
43
  "@modelcontextprotocol/sdk": "^1.27.0",
44
44
  "zod": "^3.23.0"
45
45
  },
46
+ "scripts": {
47
+ "test": "vitest run",
48
+ "test:watch": "vitest"
49
+ },
46
50
  "engines": {
47
51
  "node": ">=18"
48
52
  },
49
53
  "files": [
50
54
  "index.js",
51
55
  "README.md",
56
+ "LICENSE",
52
57
  "server.json",
58
+ "glama.json",
53
59
  "package.json"
54
60
  ],
55
61
  "devDependencies": {
56
62
  "vitest": "^4.0.18"
63
+ },
64
+ "overrides": {
65
+ "@hono/node-server": "^1.19.10",
66
+ "express-rate-limit": "^8.2.2",
67
+ "hono": "^4.12.7"
57
68
  }
58
69
  }
package/server.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.joepangallo/cloud-agent",
4
- "description": "AI software engineer — writes code, opens PRs, reviews code, generates tests, runs security scans, and answers codebase questions.",
4
+ "description": "AI software engineer — writes code, opens PRs, reviews code, generates tests, and more.",
5
5
  "repository": {
6
6
  "url": "https://github.com/joepangallo/mcp-server-cloud-agent",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.0.0",
9
+ "version": "1.1.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "mcp-server-cloud-agent",
14
- "version": "1.0.0",
14
+ "version": "1.1.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -24,7 +24,7 @@
24
24
  "name": "CLOUD_AGENT_API_KEY"
25
25
  },
26
26
  {
27
- "description": "Cloud Agent backend URL. Defaults to https://cloudagent.metaltorque.dev",
27
+ "description": "Cloud Agent backend URL. Defaults to https://agent.leddconsulting.com",
28
28
  "isRequired": false,
29
29
  "format": "string",
30
30
  "isSecret": false,