forta-mcp 1.0.0 → 1.0.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/index.js +19 -12
- package/package.json +5 -2
package/index.js
CHANGED
|
@@ -64,7 +64,7 @@ if (!API_URL || !API_TOKEN) {
|
|
|
64
64
|
|
|
65
65
|
// --- HTTP helper ---
|
|
66
66
|
|
|
67
|
-
async function api(method, path, params, body) {
|
|
67
|
+
async function api(method, path, params, body, opts_ = {}) {
|
|
68
68
|
const url = new URL(path, API_URL);
|
|
69
69
|
if (params) {
|
|
70
70
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -73,12 +73,16 @@ async function api(method, path, params, body) {
|
|
|
73
73
|
}
|
|
74
74
|
const opts = {
|
|
75
75
|
method,
|
|
76
|
-
headers: {
|
|
77
|
-
Authorization: `Bearer ${API_TOKEN}`,
|
|
78
|
-
},
|
|
76
|
+
headers: {},
|
|
79
77
|
signal: AbortSignal.timeout(30000),
|
|
80
78
|
};
|
|
81
|
-
|
|
79
|
+
// The OAuth endpoints authenticate with client credentials, not the API
|
|
80
|
+
// token, and reject a stray Authorization header.
|
|
81
|
+
if (!opts_.noAuth) opts.headers.Authorization = `Bearer ${API_TOKEN}`;
|
|
82
|
+
if (body && opts_.form) {
|
|
83
|
+
opts.headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
84
|
+
opts.body = new URLSearchParams(body).toString();
|
|
85
|
+
} else if (body) {
|
|
82
86
|
opts.headers["Content-Type"] = "application/json";
|
|
83
87
|
opts.body = JSON.stringify(body);
|
|
84
88
|
}
|
|
@@ -279,9 +283,9 @@ server.tool("forta_delete_role", "Delete a role. Users holding it lose its permi
|
|
|
279
283
|
|
|
280
284
|
// --- Grants ---
|
|
281
285
|
|
|
282
|
-
server.tool("forta_list_grants", "List access grants — which users can access which platforms, and with what role.
|
|
283
|
-
platform_id: z.number().optional().describe("Filter to one platform"),
|
|
284
|
-
user_id: z.number().optional().describe("Filter to one user"),
|
|
286
|
+
server.tool("forta_list_grants", "List access grants — which users can access which platforms, and with what role. The first place to look when a user gets a 403 from an appleby.cloud service. Filters are mutually exclusive, not combined: pass platform_id OR user_id, or neither to list all. Passing both silently ignores user_id", {
|
|
287
|
+
platform_id: z.number().optional().describe("Filter to one platform (takes precedence over user_id)"),
|
|
288
|
+
user_id: z.number().optional().describe("Filter to one user (ignored if platform_id is also given)"),
|
|
285
289
|
}, async ({ platform_id, user_id }) => {
|
|
286
290
|
const res = await api("GET", "/admin/grants", { platform_id, user_id });
|
|
287
291
|
return { content: text(res) };
|
|
@@ -312,10 +316,13 @@ server.tool("forta_check_grant", "Check whether the token's user holds an active
|
|
|
312
316
|
|
|
313
317
|
// --- Token introspection ---
|
|
314
318
|
|
|
315
|
-
server.tool("forta_introspect_token", "Introspect an access
|
|
316
|
-
token: z.string().describe("The access
|
|
317
|
-
|
|
318
|
-
|
|
319
|
+
server.tool("forta_introspect_token", "Introspect an access JWT (RFC 7662) — returns active, sub, exp, iat, token_type. Answers 'is the token this service rejected expired, malformed, or lacking a grant?'. Requires the calling platform's client credentials: introspection is scoped to that platform, and a token valid elsewhere returns active=false here. Only works for access JWTs — opaque frt_ API tokens always report active=false, so use forta_list_api_tokens to inspect those instead", {
|
|
320
|
+
token: z.string().describe("The access JWT to introspect"),
|
|
321
|
+
client_id: z.string().describe("Calling platform's client_id (from forta_list_platforms)"),
|
|
322
|
+
client_secret: z.string().describe("Calling platform's client_secret — shown only when the platform is created or its credentials are rotated"),
|
|
323
|
+
}, async ({ token, client_id, client_secret }) => {
|
|
324
|
+
const res = await api("POST", "/oauth/introspect", null,
|
|
325
|
+
{ token, client_id, client_secret }, { form: true, noAuth: true });
|
|
319
326
|
return { content: text(res) };
|
|
320
327
|
});
|
|
321
328
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forta-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "MCP server for the Forta OAuth2 identity provider",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
"keywords": [
|
|
14
14
|
"mcp",
|
|
15
15
|
"forta",
|
|
16
|
-
"auth",
|
|
16
|
+
"auth",
|
|
17
|
+
"oauth",
|
|
18
|
+
"identity",
|
|
19
|
+
"sso",
|
|
17
20
|
"model-context-protocol"
|
|
18
21
|
],
|
|
19
22
|
"author": "Aiden Appleby",
|