@skaleagents/swarm 0.3.0 → 0.4.0
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/README.md +26 -11
- package/dist/config.js +1 -1
- package/dist/http.d.ts +2 -0
- package/dist/http.js +134 -0
- package/dist/index.js +5 -87
- package/dist/review.d.ts +1 -1
- package/dist/review.js +2 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.js +107 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @skaleagents/swarm
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
SkaleAgents MCP server with local stdio and hosted Streamable HTTP transports.
|
|
4
|
+
Uses browser OAuth for sign-in.
|
|
5
5
|
|
|
6
6
|
Tools: `review_architecture`, `scan_iac_stub`.
|
|
7
7
|
|
|
@@ -9,9 +9,26 @@ Tools: `review_architecture`, `scan_iac_stub`.
|
|
|
9
9
|
client reads the files in its workspace and sends the relevant content through
|
|
10
10
|
the MCP tool for a structured review.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Hosted connection
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Use `https://skaleagents.com/mcp` in Claude Desktop or ChatGPT's custom connector
|
|
15
|
+
settings. Choose OAuth and leave client ID and secret fields blank. The client
|
|
16
|
+
registers itself, opens Google sign-in, and asks you to approve MCP access.
|
|
17
|
+
See [client setup](https://skaleagents.com/settings) for Cursor, Claude Code,
|
|
18
|
+
Claude Desktop, ChatGPT, and Codex instructions.
|
|
19
|
+
|
|
20
|
+
The web app hosts this endpoint using `handleMcpRequest` from
|
|
21
|
+
`@skaleagents/swarm/http`. It validates each bearer credential with the API's
|
|
22
|
+
`/api/oauth/mcp-token` endpoint before running a tool. Tokens are bound to the
|
|
23
|
+
MCP resource and cannot access unrelated API routes. Credentials are not
|
|
24
|
+
forwarded to the public bot directory.
|
|
25
|
+
|
|
26
|
+
`protectedResourceMetadata` exports the discovery response for
|
|
27
|
+
`/.well-known/oauth-protected-resource/mcp`.
|
|
28
|
+
|
|
29
|
+
## Local stdio prerequisites
|
|
30
|
+
|
|
31
|
+
1. Node.js 20 or newer. The client connects to `https://api.skaleagents.com` by default.
|
|
15
32
|
2. A browser that can open the SkaleAgents sign-in page.
|
|
16
33
|
|
|
17
34
|
The first tool call opens browser sign-in. Approve MCP access there and return
|
|
@@ -67,17 +84,14 @@ Dev without build:
|
|
|
67
84
|
}
|
|
68
85
|
```
|
|
69
86
|
|
|
70
|
-
### Option B:
|
|
87
|
+
### Option B: published package (hosted API)
|
|
71
88
|
|
|
72
89
|
```json
|
|
73
90
|
{
|
|
74
91
|
"mcpServers": {
|
|
75
92
|
"skaleagents": {
|
|
76
93
|
"command": "npx",
|
|
77
|
-
"args": ["-y", "@skaleagents/swarm"]
|
|
78
|
-
"env": {
|
|
79
|
-
"PLATFORM_API_URL": "https://api.skaleagents.com"
|
|
80
|
-
}
|
|
94
|
+
"args": ["-y", "@skaleagents/swarm@0.4.0"]
|
|
81
95
|
}
|
|
82
96
|
}
|
|
83
97
|
}
|
|
@@ -87,16 +101,17 @@ Restart Cursor after saving. In Agent/Chat, tools should appear as `review_archi
|
|
|
87
101
|
|
|
88
102
|
## Claude Code
|
|
89
103
|
|
|
90
|
-
|
|
104
|
+
Use the published package configuration above in `.mcp.json`. OAuth starts on the first tool call. No API URL or keys are needed.
|
|
91
105
|
|
|
92
106
|
## Environment
|
|
93
107
|
|
|
94
108
|
| Variable | Required | Description |
|
|
95
109
|
|----------|----------|-------------|
|
|
96
110
|
| `SKALEAGENTS_API_TOKEN` | No | Legacy Sanctum bearer-token override. OAuth is used when empty. |
|
|
97
|
-
| `PLATFORM_API_URL` | No |
|
|
111
|
+
| `PLATFORM_API_URL` | No | Defaults to `https://api.skaleagents.com`. Override only for local development or another API deployment. Empty values use the default. |
|
|
98
112
|
| `SKALEAGENTS_OAUTH_CACHE` | No | OAuth cache path. Default `~/.config/skaleagents/oauth.json`. |
|
|
99
113
|
| `SKALEAGENTS_OAUTH_ENABLED` | No | Set to `false` only to disable browser OAuth. |
|
|
114
|
+
| `MCP_RESOURCE_URL` | No | Hosted transport audience. Defaults to `https://skaleagents.com/mcp`; must match the API OAuth configuration. |
|
|
100
115
|
|
|
101
116
|
## Auth behavior
|
|
102
117
|
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
export function getApiUrl() {
|
|
4
|
-
return (process.env.PLATFORM_API_URL?.replace(
|
|
4
|
+
return (process.env.PLATFORM_API_URL?.trim().replace(/\/+$/, "") || "https://api.skaleagents.com");
|
|
5
5
|
}
|
|
6
6
|
export function getApiToken() {
|
|
7
7
|
return process.env.SKALEAGENTS_API_TOKEN?.trim() ?? "";
|
package/dist/http.d.ts
ADDED
package/dist/http.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
2
|
+
import { createServer } from "./server.js";
|
|
3
|
+
import { getApiUrl } from "./config.js";
|
|
4
|
+
const resource = process.env.MCP_RESOURCE_URL ?? "https://skaleagents.com/mcp";
|
|
5
|
+
const metadataUrl = `${new URL(resource).origin}/.well-known/oauth-protected-resource/mcp`;
|
|
6
|
+
const allowedOrigins = new Set([
|
|
7
|
+
"https://claude.ai",
|
|
8
|
+
"https://chatgpt.com",
|
|
9
|
+
"https://platform.openai.com",
|
|
10
|
+
new URL(resource).origin,
|
|
11
|
+
]);
|
|
12
|
+
const headers = {
|
|
13
|
+
"Cache-Control": "no-store",
|
|
14
|
+
"Access-Control-Allow-Origin": "*",
|
|
15
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
16
|
+
"Access-Control-Allow-Headers": "Authorization, Content-Type, Accept, MCP-Protocol-Version, MCP-Session-Id, Last-Event-ID",
|
|
17
|
+
"Access-Control-Expose-Headers": "WWW-Authenticate, MCP-Session-Id, MCP-Protocol-Version",
|
|
18
|
+
};
|
|
19
|
+
function json(status, body, extraHeaders = {}) {
|
|
20
|
+
return Response.json(body, {
|
|
21
|
+
status,
|
|
22
|
+
headers: { ...headers, ...extraHeaders },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function challenge() {
|
|
26
|
+
return json(401, { error: "unauthorized" }, {
|
|
27
|
+
"WWW-Authenticate": `Bearer resource_metadata="${metadataUrl}", scope="mcp"`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function protectedResourceMetadata() {
|
|
31
|
+
return json(200, {
|
|
32
|
+
resource,
|
|
33
|
+
authorization_servers: [getApiUrl()],
|
|
34
|
+
scopes_supported: ["mcp"],
|
|
35
|
+
bearer_methods_supported: ["header"],
|
|
36
|
+
resource_documentation: "https://skaleagents.com/settings",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export async function handleMcpRequest(request) {
|
|
40
|
+
const origin = request.headers.get("origin");
|
|
41
|
+
if (origin && !allowedOrigins.has(origin))
|
|
42
|
+
return json(403, { error: "origin_not_allowed" });
|
|
43
|
+
if (request.method === "OPTIONS")
|
|
44
|
+
return new Response(null, { status: 204, headers });
|
|
45
|
+
const authorization = request.headers.get("authorization");
|
|
46
|
+
if (!authorization || !/^Bearer [^\s]+$/i.test(authorization))
|
|
47
|
+
return challenge();
|
|
48
|
+
try {
|
|
49
|
+
// Validate the opaque token with its issuer, including the MCP audience.
|
|
50
|
+
const validation = await fetch(`${getApiUrl()}/api/oauth/mcp-token`, {
|
|
51
|
+
headers: { Authorization: authorization, Accept: "application/json" },
|
|
52
|
+
signal: AbortSignal.timeout(10_000),
|
|
53
|
+
redirect: "error",
|
|
54
|
+
cache: "no-store",
|
|
55
|
+
});
|
|
56
|
+
if (validation.status === 401 || validation.status === 403)
|
|
57
|
+
return challenge();
|
|
58
|
+
if (!validation.ok)
|
|
59
|
+
return json(503, { error: "authorization_unavailable" });
|
|
60
|
+
const token = (await validation.json());
|
|
61
|
+
if (!token.active ||
|
|
62
|
+
token.resource !== resource ||
|
|
63
|
+
token.scope !== "mcp" ||
|
|
64
|
+
!token.expiresAt ||
|
|
65
|
+
token.expiresAt * 1000 <= Date.now()) {
|
|
66
|
+
return challenge();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return json(503, { error: "authorization_unavailable" });
|
|
71
|
+
}
|
|
72
|
+
if (request.method !== "POST")
|
|
73
|
+
return json(405, { error: "method_not_allowed" }, { Allow: "POST, OPTIONS" });
|
|
74
|
+
if (!request.headers.get("content-type")?.startsWith("application/json"))
|
|
75
|
+
return json(415, { error: "expected_json" });
|
|
76
|
+
let body;
|
|
77
|
+
try {
|
|
78
|
+
const reader = request.body?.getReader();
|
|
79
|
+
if (!reader)
|
|
80
|
+
return json(400, { error: "missing_body" });
|
|
81
|
+
const chunks = [];
|
|
82
|
+
let size = 0;
|
|
83
|
+
try {
|
|
84
|
+
for (;;) {
|
|
85
|
+
const { value, done } = await reader.read();
|
|
86
|
+
if (done)
|
|
87
|
+
break;
|
|
88
|
+
size += value.byteLength;
|
|
89
|
+
if (size > 2_100_000) {
|
|
90
|
+
await reader.cancel();
|
|
91
|
+
return json(413, { error: "request_too_large" });
|
|
92
|
+
}
|
|
93
|
+
chunks.push(value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
reader.releaseLock();
|
|
98
|
+
}
|
|
99
|
+
body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return json(400, {
|
|
103
|
+
jsonrpc: "2.0",
|
|
104
|
+
id: null,
|
|
105
|
+
error: { code: -32700, message: "Invalid JSON" },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const server = createServer(true);
|
|
109
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
110
|
+
sessionIdGenerator: undefined,
|
|
111
|
+
enableJsonResponse: true,
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
await server.connect(transport);
|
|
115
|
+
const response = await transport.handleRequest(request, {
|
|
116
|
+
parsedBody: body,
|
|
117
|
+
});
|
|
118
|
+
// Finish the JSON response before closing this request-scoped transport.
|
|
119
|
+
const responseBody = await response.arrayBuffer();
|
|
120
|
+
const responseHeaders = new Headers(response.headers);
|
|
121
|
+
for (const [key, value] of Object.entries(headers))
|
|
122
|
+
responseHeaders.set(key, value);
|
|
123
|
+
return new Response(responseBody.byteLength ? responseBody : null, {
|
|
124
|
+
status: response.status,
|
|
125
|
+
headers: responseHeaders,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return json(500, { error: "mcp_request_failed" });
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await server.close();
|
|
133
|
+
}
|
|
134
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,91 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
version: "0.3.0",
|
|
10
|
-
});
|
|
11
|
-
server.registerTool("review_architecture", {
|
|
12
|
-
title: "Review architecture",
|
|
13
|
-
description: "Review application source or infrastructure text for security, reliability, and cost risks.",
|
|
14
|
-
inputSchema: {
|
|
15
|
-
content: z
|
|
16
|
-
.string()
|
|
17
|
-
.min(1)
|
|
18
|
-
.max(500_000)
|
|
19
|
-
.describe("Application source or IaC text to review"),
|
|
20
|
-
focus: z
|
|
21
|
-
.enum(["security", "reliability", "cost", "general"])
|
|
22
|
-
.optional()
|
|
23
|
-
.default("general")
|
|
24
|
-
.describe("Review focus: security, reliability, cost, general"),
|
|
25
|
-
format: z
|
|
26
|
-
.enum(["terraform", "cloudformation", "kubernetes", "application", "auto"])
|
|
27
|
-
.optional()
|
|
28
|
-
.default("auto")
|
|
29
|
-
.describe("Content format: terraform, cloudformation, kubernetes, application, auto"),
|
|
30
|
-
},
|
|
31
|
-
}, async ({ content, focus, format }) => {
|
|
32
|
-
const auth = await requireApiAuth();
|
|
33
|
-
if (!auth.ok)
|
|
34
|
-
return unauthorizedContent(auth);
|
|
35
|
-
const token = await getApiAccessToken();
|
|
36
|
-
if (!token)
|
|
37
|
-
return unauthorizedContent({ ok: false, reason: "oauth_failed" });
|
|
38
|
-
const botHints = await fetchPublicBotHints(token);
|
|
39
|
-
const focusValue = focus ?? "general";
|
|
40
|
-
const formatValue = format ?? "auto";
|
|
41
|
-
const output = {
|
|
42
|
-
summary: `Structured architecture review from @skaleagents/swarm (focus=${focusValue}, format=${formatValue})`,
|
|
43
|
-
findings: architectureFindings(content, focusValue),
|
|
44
|
-
botHints,
|
|
45
|
-
};
|
|
46
|
-
return {
|
|
47
|
-
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
server.registerTool("scan_iac_stub", {
|
|
51
|
-
title: "Scan IaC (stub)",
|
|
52
|
-
description: "Stub seam for Phase 2 DevSecOps IaC scanning.",
|
|
53
|
-
inputSchema: {
|
|
54
|
-
content: z
|
|
55
|
-
.string()
|
|
56
|
-
.min(1)
|
|
57
|
-
.max(500_000)
|
|
58
|
-
.describe("Terraform / CloudFormation / Kubernetes YAML"),
|
|
59
|
-
format: z
|
|
60
|
-
.string()
|
|
61
|
-
.optional()
|
|
62
|
-
.describe("terraform, cloudformation, kubernetes, auto"),
|
|
63
|
-
},
|
|
64
|
-
}, async ({ content, format }) => {
|
|
65
|
-
const auth = await requireApiAuth();
|
|
66
|
-
if (!auth.ok)
|
|
67
|
-
return unauthorizedContent(auth);
|
|
68
|
-
const formatValue = format === "terraform" ||
|
|
69
|
-
format === "cloudformation" ||
|
|
70
|
-
format === "kubernetes" ||
|
|
71
|
-
format === "auto"
|
|
72
|
-
? format
|
|
73
|
-
: "auto";
|
|
74
|
-
const output = {
|
|
75
|
-
status: "stub",
|
|
76
|
-
message: "Full IaC scanning lands in Phase 2 agent-swarm",
|
|
77
|
-
format: formatValue,
|
|
78
|
-
parsedResourceCount: countIacResources(content),
|
|
79
|
-
};
|
|
80
|
-
return {
|
|
81
|
-
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
82
|
-
};
|
|
83
|
-
});
|
|
84
|
-
async function main() {
|
|
85
|
-
const transport = new StdioServerTransport();
|
|
86
|
-
await server.connect(transport);
|
|
87
|
-
}
|
|
88
|
-
main().catch((err) => {
|
|
89
|
-
console.error(err);
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
createServer()
|
|
5
|
+
.connect(new StdioServerTransport())
|
|
6
|
+
.catch((error) => {
|
|
7
|
+
console.error(error);
|
|
90
8
|
process.exit(1);
|
|
91
9
|
});
|
package/dist/review.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export type Finding = {
|
|
|
5
5
|
detail: string;
|
|
6
6
|
};
|
|
7
7
|
export declare function architectureFindings(content: string, focus: string): Finding[];
|
|
8
|
-
export declare function fetchPublicBotHints(token
|
|
8
|
+
export declare function fetchPublicBotHints(token?: string): Promise<string[]>;
|
|
9
9
|
export declare function countIacResources(content: string): number;
|
package/dist/review.js
CHANGED
|
@@ -107,9 +107,10 @@ export function architectureFindings(content, focus) {
|
|
|
107
107
|
export async function fetchPublicBotHints(token) {
|
|
108
108
|
try {
|
|
109
109
|
const res = await fetch(`${getApiUrl()}/api/bots`, {
|
|
110
|
+
signal: AbortSignal.timeout(10_000),
|
|
110
111
|
headers: {
|
|
111
112
|
Accept: "application/json",
|
|
112
|
-
Authorization: `Bearer ${token}
|
|
113
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
113
114
|
},
|
|
114
115
|
});
|
|
115
116
|
if (!res.ok)
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { getApiAccessToken, requireApiAuth, unauthorizedContent, } from "./auth.js";
|
|
4
|
+
import { architectureFindings, countIacResources, fetchPublicBotHints, } from "./review.js";
|
|
5
|
+
export function createServer(remote = false) {
|
|
6
|
+
const server = new McpServer({
|
|
7
|
+
name: "skaleagents-swarm",
|
|
8
|
+
version: "0.4.0",
|
|
9
|
+
});
|
|
10
|
+
server.registerTool("review_architecture", {
|
|
11
|
+
title: "Review architecture",
|
|
12
|
+
description: "Review application source or infrastructure text for security, reliability, and cost risks.",
|
|
13
|
+
annotations: {
|
|
14
|
+
readOnlyHint: true,
|
|
15
|
+
destructiveHint: false,
|
|
16
|
+
openWorldHint: false,
|
|
17
|
+
},
|
|
18
|
+
_meta: { securitySchemes: [{ type: "oauth2", scopes: ["mcp"] }] },
|
|
19
|
+
inputSchema: {
|
|
20
|
+
content: z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(500_000)
|
|
24
|
+
.describe("Application source or IaC text to review"),
|
|
25
|
+
focus: z
|
|
26
|
+
.enum(["security", "reliability", "cost", "general"])
|
|
27
|
+
.optional()
|
|
28
|
+
.default("general")
|
|
29
|
+
.describe("Review focus: security, reliability, cost, general"),
|
|
30
|
+
format: z
|
|
31
|
+
.enum([
|
|
32
|
+
"terraform",
|
|
33
|
+
"cloudformation",
|
|
34
|
+
"kubernetes",
|
|
35
|
+
"application",
|
|
36
|
+
"auto",
|
|
37
|
+
])
|
|
38
|
+
.optional()
|
|
39
|
+
.default("auto")
|
|
40
|
+
.describe("Content format: terraform, cloudformation, kubernetes, application, auto"),
|
|
41
|
+
},
|
|
42
|
+
}, async ({ content, focus, format }) => {
|
|
43
|
+
let token;
|
|
44
|
+
if (!remote) {
|
|
45
|
+
const auth = await requireApiAuth();
|
|
46
|
+
if (!auth.ok)
|
|
47
|
+
return unauthorizedContent(auth);
|
|
48
|
+
token = (await getApiAccessToken()) ?? undefined;
|
|
49
|
+
if (!token)
|
|
50
|
+
return unauthorizedContent({ ok: false, reason: "oauth_failed" });
|
|
51
|
+
}
|
|
52
|
+
const botHints = await fetchPublicBotHints(token);
|
|
53
|
+
const focusValue = focus ?? "general";
|
|
54
|
+
const formatValue = format ?? "auto";
|
|
55
|
+
const output = {
|
|
56
|
+
summary: `Structured architecture review from @skaleagents/swarm (focus=${focusValue}, format=${formatValue})`,
|
|
57
|
+
findings: architectureFindings(content, focusValue),
|
|
58
|
+
botHints,
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
server.registerTool("scan_iac_stub", {
|
|
65
|
+
title: "Scan IaC (stub)",
|
|
66
|
+
description: "Stub seam for Phase 2 DevSecOps IaC scanning.",
|
|
67
|
+
annotations: {
|
|
68
|
+
readOnlyHint: true,
|
|
69
|
+
destructiveHint: false,
|
|
70
|
+
openWorldHint: false,
|
|
71
|
+
},
|
|
72
|
+
_meta: { securitySchemes: [{ type: "oauth2", scopes: ["mcp"] }] },
|
|
73
|
+
inputSchema: {
|
|
74
|
+
content: z
|
|
75
|
+
.string()
|
|
76
|
+
.min(1)
|
|
77
|
+
.max(500_000)
|
|
78
|
+
.describe("Terraform / CloudFormation / Kubernetes YAML"),
|
|
79
|
+
format: z
|
|
80
|
+
.string()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("terraform, cloudformation, kubernetes, auto"),
|
|
83
|
+
},
|
|
84
|
+
}, async ({ content, format }) => {
|
|
85
|
+
if (!remote) {
|
|
86
|
+
const auth = await requireApiAuth();
|
|
87
|
+
if (!auth.ok)
|
|
88
|
+
return unauthorizedContent(auth);
|
|
89
|
+
}
|
|
90
|
+
const formatValue = format === "terraform" ||
|
|
91
|
+
format === "cloudformation" ||
|
|
92
|
+
format === "kubernetes" ||
|
|
93
|
+
format === "auto"
|
|
94
|
+
? format
|
|
95
|
+
: "auto";
|
|
96
|
+
const output = {
|
|
97
|
+
status: "stub",
|
|
98
|
+
message: "Full IaC scanning lands in Phase 2 agent-swarm",
|
|
99
|
+
format: formatValue,
|
|
100
|
+
parsedResourceCount: countIacResources(content),
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
return server;
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skaleagents/swarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"description": "SkaleAgents
|
|
9
|
+
"description": "SkaleAgents review tools with OAuth for local and hosted MCP clients",
|
|
10
|
+
"exports": {
|
|
11
|
+
"./http": {
|
|
12
|
+
"types": "./dist/http.d.ts",
|
|
13
|
+
"import": "./dist/http.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
10
16
|
"bin": {
|
|
11
17
|
"skaleagents-swarm": "dist/index.js"
|
|
12
18
|
},
|