@ferrlabs/mcp 6.5.5 → 7.0.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/dist/tools/__tests__/orgs.test.js +3 -3
- package/dist/tools/issues.js +1 -1
- package/dist/tools/me.js +4 -4
- package/dist/tools/org-admin.js +1 -1
- package/dist/tools/orgs.js +2 -2
- package/dist/tools/subscriptions.js +4 -4
- package/dist/tools/tokens.js +4 -4
- package/dist/tools/vaults.js +1 -1
- package/package.json +2 -2
|
@@ -28,7 +28,7 @@ describe('orgs tools', () => {
|
|
|
28
28
|
expect(handlers.has('list_orgs')).toBe(true);
|
|
29
29
|
expect(handlers.has('list_projects')).toBe(true);
|
|
30
30
|
});
|
|
31
|
-
it('list_orgs hits /
|
|
31
|
+
it('list_orgs hits /orgs with the API token', async () => {
|
|
32
32
|
mockFetch.mockResolvedValue(makeResponse([
|
|
33
33
|
{
|
|
34
34
|
id: 'o1',
|
|
@@ -40,7 +40,7 @@ describe('orgs tools', () => {
|
|
|
40
40
|
]));
|
|
41
41
|
const result = await handlers.get('list_orgs')({});
|
|
42
42
|
const [url, init] = mockFetch.mock.calls[0];
|
|
43
|
-
expect(url).toContain('/
|
|
43
|
+
expect(url).toContain('/orgs');
|
|
44
44
|
expect(init.headers['x-api-token']).toBe('test-token');
|
|
45
45
|
expect(result).toMatchObject({ content: [{ type: 'text' }] });
|
|
46
46
|
});
|
|
@@ -48,6 +48,6 @@ describe('orgs tools', () => {
|
|
|
48
48
|
mockFetch.mockResolvedValue(makeResponse([]));
|
|
49
49
|
await handlers.get('list_projects')({ org_slug: 'acme corp' });
|
|
50
50
|
const [url] = mockFetch.mock.calls[0];
|
|
51
|
-
expect(url).toContain('/
|
|
51
|
+
expect(url).toContain('/orgs/acme%20corp/projects');
|
|
52
52
|
});
|
|
53
53
|
});
|
package/dist/tools/issues.js
CHANGED
|
@@ -21,7 +21,7 @@ export function registerIssuesTools(server) {
|
|
|
21
21
|
if (per_page !== undefined)
|
|
22
22
|
params.set('per_page', String(per_page));
|
|
23
23
|
const qs = params.toString();
|
|
24
|
-
const issues = await apiRequest(`/
|
|
24
|
+
const issues = await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/projects/${encodeURIComponent(project_slug)}/issues${qs ? `?${qs}` : ''}`, { token });
|
|
25
25
|
return {
|
|
26
26
|
content: [{ type: 'text', text: JSON.stringify(issues, null, 2) }],
|
|
27
27
|
};
|
package/dist/tools/me.js
CHANGED
|
@@ -17,7 +17,7 @@ export function registerMeTools(server) {
|
|
|
17
17
|
if (v !== undefined)
|
|
18
18
|
body[k] = v;
|
|
19
19
|
}
|
|
20
|
-
const me = await apiRequest('/
|
|
20
|
+
const me = await apiRequest('/auth/me', {
|
|
21
21
|
token,
|
|
22
22
|
method: 'PATCH',
|
|
23
23
|
body,
|
|
@@ -26,14 +26,14 @@ export function registerMeTools(server) {
|
|
|
26
26
|
});
|
|
27
27
|
server.tool('list_my_sessions', 'List your active sessions across devices/browsers. The session you used to obtain the current token is flagged `current: true`.', {}, async () => {
|
|
28
28
|
const token = await getToken();
|
|
29
|
-
const sessions = await apiRequest('/
|
|
29
|
+
const sessions = await apiRequest('/me/sessions', { token });
|
|
30
30
|
return { content: [{ type: 'text', text: JSON.stringify(sessions, null, 2) }] };
|
|
31
31
|
});
|
|
32
32
|
server.tool('revoke_my_session', 'Revoke one of your active sessions by id (sign out that device). Refusing to revoke the current session is left up to the API.', {
|
|
33
33
|
session_id: z.string().min(1).describe('Session id (from list_my_sessions)'),
|
|
34
34
|
}, async ({ session_id }) => {
|
|
35
35
|
const token = await getToken();
|
|
36
|
-
await apiRequest(`/
|
|
36
|
+
await apiRequest(`/me/sessions/${encodeURIComponent(session_id)}`, {
|
|
37
37
|
token,
|
|
38
38
|
method: 'DELETE',
|
|
39
39
|
});
|
|
@@ -41,7 +41,7 @@ export function registerMeTools(server) {
|
|
|
41
41
|
});
|
|
42
42
|
server.tool('export_my_account', "Trigger an export of the authenticated user's data (GDPR Article 15). Returns the export bundle, which can be large; consider redirecting the LLM context to a summary.", {}, async () => {
|
|
43
43
|
const token = await getToken();
|
|
44
|
-
const dump = await apiRequest('/
|
|
44
|
+
const dump = await apiRequest('/auth/me/export', { token });
|
|
45
45
|
return { content: [{ type: 'text', text: JSON.stringify(dump, null, 2) }] };
|
|
46
46
|
});
|
|
47
47
|
}
|
package/dist/tools/org-admin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { apiRequest, getToken } from '@ferrlabs/mcp-core';
|
|
3
3
|
function orgBase(slug) {
|
|
4
|
-
return `/
|
|
4
|
+
return `/orgs/${encodeURIComponent(slug)}`;
|
|
5
5
|
}
|
|
6
6
|
export function registerOrgAdminTools(server) {
|
|
7
7
|
server.tool('get_org', 'Get a single FerrLabs organization by slug.', {
|
package/dist/tools/orgs.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getToken } from '@ferrlabs/mcp-core';
|
|
|
4
4
|
export function registerOrgsTools(server) {
|
|
5
5
|
server.tool('list_orgs', 'List FerrLabs organizations the authenticated user belongs to', {}, async () => {
|
|
6
6
|
const token = await getToken();
|
|
7
|
-
const orgs = await apiRequest('/
|
|
7
|
+
const orgs = await apiRequest('/orgs', { token });
|
|
8
8
|
return {
|
|
9
9
|
content: [{ type: 'text', text: JSON.stringify(orgs, null, 2) }],
|
|
10
10
|
};
|
|
@@ -13,7 +13,7 @@ export function registerOrgsTools(server) {
|
|
|
13
13
|
org_slug: z.string().min(1).describe('Organization slug (from list_orgs)'),
|
|
14
14
|
}, async ({ org_slug }) => {
|
|
15
15
|
const token = await getToken();
|
|
16
|
-
const projects = await apiRequest(`/
|
|
16
|
+
const projects = await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/projects`, { token });
|
|
17
17
|
return {
|
|
18
18
|
content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }],
|
|
19
19
|
};
|
|
@@ -6,7 +6,7 @@ export function registerSubscriptionsTools(server) {
|
|
|
6
6
|
org_slug: z.string().min(1).describe('Organization slug'),
|
|
7
7
|
}, async ({ org_slug }) => {
|
|
8
8
|
const token = await getToken();
|
|
9
|
-
const subs = await apiRequest(`/
|
|
9
|
+
const subs = await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/subscriptions`, { token });
|
|
10
10
|
return {
|
|
11
11
|
content: [{ type: 'text', text: JSON.stringify(subs, null, 2) }],
|
|
12
12
|
};
|
|
@@ -19,7 +19,7 @@ export function registerSubscriptionsTools(server) {
|
|
|
19
19
|
tier: z.enum(TIERS).default('free').describe('Tier (default free).'),
|
|
20
20
|
}, async ({ org_slug, product, tier }) => {
|
|
21
21
|
const token = await getToken();
|
|
22
|
-
const sub = await apiRequest(`/
|
|
22
|
+
const sub = await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/subscriptions`, { token, method: 'POST', body: { product, tier } });
|
|
23
23
|
return { content: [{ type: 'text', text: JSON.stringify(sub, null, 2) }] };
|
|
24
24
|
});
|
|
25
25
|
server.tool('update_subscription', 'Change the tier of an active product subscription (upgrade/downgrade). Stripe proration is applied per the API config.', {
|
|
@@ -28,7 +28,7 @@ export function registerSubscriptionsTools(server) {
|
|
|
28
28
|
tier: z.enum(TIERS),
|
|
29
29
|
}, async ({ org_slug, product, tier }) => {
|
|
30
30
|
const token = await getToken();
|
|
31
|
-
const sub = await apiRequest(`/
|
|
31
|
+
const sub = await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/subscriptions/${encodeURIComponent(product)}`, { token, method: 'PATCH', body: { tier } });
|
|
32
32
|
return { content: [{ type: 'text', text: JSON.stringify(sub, null, 2) }] };
|
|
33
33
|
});
|
|
34
34
|
server.tool('cancel_subscription', 'Cancel a product subscription on an organization. By default the cancellation takes effect at the end of the current billing period; pass immediate=true to cut it off now (with proration).', {
|
|
@@ -41,7 +41,7 @@ export function registerSubscriptionsTools(server) {
|
|
|
41
41
|
}, async ({ org_slug, product, immediate }) => {
|
|
42
42
|
const token = await getToken();
|
|
43
43
|
const qs = immediate ? '?immediate=true' : '';
|
|
44
|
-
await apiRequest(`/
|
|
44
|
+
await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/subscriptions/${encodeURIComponent(product)}${qs}`, { token, method: 'DELETE' });
|
|
45
45
|
return {
|
|
46
46
|
content: [
|
|
47
47
|
{
|
package/dist/tools/tokens.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getToken } from '@ferrlabs/mcp-core';
|
|
|
4
4
|
export function registerTokenTools(server) {
|
|
5
5
|
server.tool('get_me', 'Get the current authenticated FerrLabs user profile', {}, async () => {
|
|
6
6
|
const token = await getToken();
|
|
7
|
-
const user = await apiRequest('/
|
|
7
|
+
const user = await apiRequest('/auth/me', { token });
|
|
8
8
|
return {
|
|
9
9
|
content: [
|
|
10
10
|
{
|
|
@@ -16,7 +16,7 @@ export function registerTokenTools(server) {
|
|
|
16
16
|
});
|
|
17
17
|
server.tool('list_tokens', 'List all API tokens for the authenticated user', {}, async () => {
|
|
18
18
|
const token = await getToken();
|
|
19
|
-
const tokens = await apiRequest('/
|
|
19
|
+
const tokens = await apiRequest('/auth/tokens', { token });
|
|
20
20
|
return {
|
|
21
21
|
content: [
|
|
22
22
|
{
|
|
@@ -32,7 +32,7 @@ export function registerTokenTools(server) {
|
|
|
32
32
|
expires_at: z.string().optional().describe('Expiration date (ISO 8601)'),
|
|
33
33
|
}, async ({ name, scopes, expires_at }) => {
|
|
34
34
|
const token = await getToken();
|
|
35
|
-
const result = await apiRequest('/
|
|
35
|
+
const result = await apiRequest('/auth/tokens', {
|
|
36
36
|
method: 'POST',
|
|
37
37
|
body: { name, scopes, expires_at },
|
|
38
38
|
token,
|
|
@@ -51,7 +51,7 @@ export function registerTokenTools(server) {
|
|
|
51
51
|
token_id: z.string().uuid().describe('Token ID to revoke'),
|
|
52
52
|
}, async ({ token_id }) => {
|
|
53
53
|
const token = await getToken();
|
|
54
|
-
await apiRequest(`/
|
|
54
|
+
await apiRequest(`/auth/tokens/${token_id}`, {
|
|
55
55
|
method: 'DELETE',
|
|
56
56
|
token,
|
|
57
57
|
});
|
package/dist/tools/vaults.js
CHANGED
|
@@ -7,7 +7,7 @@ export function registerVaultsTools(server) {
|
|
|
7
7
|
project_slug: z.string().min(1).describe('Project slug'),
|
|
8
8
|
}, async ({ org_slug, project_slug }) => {
|
|
9
9
|
const token = await getToken();
|
|
10
|
-
const vaults = await apiRequest(`/
|
|
10
|
+
const vaults = await apiRequest(`/orgs/${encodeURIComponent(org_slug)}/projects/${encodeURIComponent(project_slug)}/vaults`, { token });
|
|
11
11
|
return {
|
|
12
12
|
content: [{ type: 'text', text: JSON.stringify(vaults, null, 2) }],
|
|
13
13
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ferrlabs/mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Model Context Protocol server for the FerrLabs platform (orgs, projects, subscriptions, docs, tokens, public stats)",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
25
25
|
"zod": "^4.3.6",
|
|
26
|
-
"@ferrlabs/mcp-core": "^
|
|
26
|
+
"@ferrlabs/mcp-core": "^7.0.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|