@tankpkg/mcp-server 0.4.2
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 +21 -0
- package/README.md +132 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-client.d.ts +44 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +78 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/config.d.ts +25 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +59 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/packer.d.ts +14 -0
- package/dist/lib/packer.d.ts.map +1 -0
- package/dist/lib/packer.js +191 -0
- package/dist/lib/packer.js.map +1 -0
- package/dist/tools/login.d.ts +3 -0
- package/dist/tools/login.d.ts.map +1 -0
- package/dist/tools/login.js +104 -0
- package/dist/tools/login.js.map +1 -0
- package/dist/tools/publish-skill.d.ts +3 -0
- package/dist/tools/publish-skill.d.ts.map +1 -0
- package/dist/tools/publish-skill.js +166 -0
- package/dist/tools/publish-skill.js.map +1 -0
- package/dist/tools/scan-skill.d.ts +3 -0
- package/dist/tools/scan-skill.d.ts.map +1 -0
- package/dist/tools/scan-skill.js +148 -0
- package/dist/tools/scan-skill.js.map +1 -0
- package/dist/tools/search-skills.d.ts +3 -0
- package/dist/tools/search-skills.d.ts.map +1 -0
- package/dist/tools/search-skills.js +54 -0
- package/dist/tools/search-skills.js.map +1 -0
- package/dist/tools/skill-info.d.ts +3 -0
- package/dist/tools/skill-info.d.ts.map +1 -0
- package/dist/tools/skill-info.js +88 -0
- package/dist/tools/skill-info.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getConfig, setConfig } from '../lib/config.js';
|
|
3
|
+
import { TankApiClient } from '../lib/api-client.js';
|
|
4
|
+
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
6
|
+
export function registerLoginTool(server) {
|
|
7
|
+
server.tool('login', 'Authenticate with Tank using GitHub OAuth device flow. Opens browser for authorization.', {
|
|
8
|
+
timeout: z.number().optional().describe('Timeout in milliseconds (default: 300000 = 5 minutes)'),
|
|
9
|
+
}, async ({ timeout = DEFAULT_TIMEOUT_MS }) => {
|
|
10
|
+
const client = new TankApiClient();
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
// Check if already logged in with valid token
|
|
13
|
+
if (config.token) {
|
|
14
|
+
const authCheck = await client.verifyAuth();
|
|
15
|
+
if (authCheck.valid) {
|
|
16
|
+
const displayName = authCheck.user?.name ?? authCheck.user?.email ?? 'unknown user';
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: `Already logged in as ${displayName}.\n\nTo log out, delete ~/.tank/config.json or use the CLI: tank logout`,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Start device flow
|
|
28
|
+
const state = crypto.randomUUID();
|
|
29
|
+
const startRes = await fetch(`${config.registry}/api/v1/cli-auth/start`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
body: JSON.stringify({ state }),
|
|
33
|
+
});
|
|
34
|
+
if (!startRes.ok) {
|
|
35
|
+
const body = await startRes.json().catch(() => ({}));
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: `Failed to start login flow: ${body.error ?? startRes.statusText}`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const { authUrl, sessionCode } = (await startRes.json());
|
|
46
|
+
// Return auth URL and poll for completion
|
|
47
|
+
const deadline = Date.now() + timeout;
|
|
48
|
+
let authorized = false;
|
|
49
|
+
let lastStatus = '';
|
|
50
|
+
while (Date.now() < deadline) {
|
|
51
|
+
try {
|
|
52
|
+
const exchangeRes = await fetch(`${config.registry}/api/v1/cli-auth/exchange`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify({ sessionCode, state }),
|
|
56
|
+
});
|
|
57
|
+
if (exchangeRes.ok) {
|
|
58
|
+
const { token, user } = (await exchangeRes.json());
|
|
59
|
+
// Save token to config
|
|
60
|
+
setConfig({ token, user: user });
|
|
61
|
+
const displayName = user.name ?? user.email ?? 'unknown user';
|
|
62
|
+
return {
|
|
63
|
+
content: [
|
|
64
|
+
{
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: `Successfully logged in as ${displayName}!\n\nYou can now use all Tank MCP tools: scan-skill, publish-skill, etc.`,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// 400 means not yet authorized - keep polling
|
|
72
|
+
if (exchangeRes.status !== 400) {
|
|
73
|
+
const body = await exchangeRes.json().catch(() => ({}));
|
|
74
|
+
return {
|
|
75
|
+
content: [
|
|
76
|
+
{
|
|
77
|
+
type: 'text',
|
|
78
|
+
text: `Login failed: ${body.error ?? exchangeRes.statusText}`,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Check status and provide updates
|
|
84
|
+
const newStatus = 'Waiting for authorization...';
|
|
85
|
+
if (newStatus !== lastStatus) {
|
|
86
|
+
lastStatus = newStatus;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Network errors during polling are transient
|
|
91
|
+
}
|
|
92
|
+
await new Promise((resolve) => setTimeout(resolve, DEFAULT_POLL_INTERVAL_MS));
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: 'text',
|
|
98
|
+
text: `Login timed out. The authorization link may have expired.\n\nTry again: tank login`,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/tools/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-skill.d.ts","sourceRoot":"","sources":["../../src/tools/publish-skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqBzE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmLhE"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { TankApiClient } from '../lib/api-client.js';
|
|
4
|
+
import { pack } from '../lib/packer.js';
|
|
5
|
+
import { getConfig } from '../lib/config.js';
|
|
6
|
+
export function registerPublishSkillTool(server) {
|
|
7
|
+
server.tool('publish-skill', 'Publish a skill to the Tank registry. Requires authentication.', {
|
|
8
|
+
directory: z.string().optional().describe('Directory to publish (default: current directory)'),
|
|
9
|
+
visibility: z.enum(['public', 'private']).optional().default('public').describe('Package visibility'),
|
|
10
|
+
dryRun: z.boolean().optional().default(false).describe('Validate without publishing'),
|
|
11
|
+
}, async ({ directory = '.', visibility = 'public', dryRun = false }) => {
|
|
12
|
+
const absDir = path.resolve(directory);
|
|
13
|
+
const client = new TankApiClient();
|
|
14
|
+
const config = getConfig();
|
|
15
|
+
// Check auth (skip for dry run)
|
|
16
|
+
if (!dryRun && !client.isAuthenticated) {
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: 'You need to log in first. Use the login tool to authenticate with Tank.\n\nExample: "Log in to Tank"',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (!dryRun) {
|
|
27
|
+
const authCheck = await client.verifyAuth();
|
|
28
|
+
if (!authCheck.valid) {
|
|
29
|
+
return {
|
|
30
|
+
content: [
|
|
31
|
+
{
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: 'Your session has expired. Use the login tool to authenticate again.',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// Pack the skill
|
|
40
|
+
let packResult;
|
|
41
|
+
try {
|
|
42
|
+
packResult = await pack(absDir);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: `Failed to pack skill: ${err instanceof Error ? err.message : String(err)}`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const manifest = packResult.manifest;
|
|
55
|
+
const skillName = manifest.name ?? 'unknown';
|
|
56
|
+
const skillVersion = manifest.version ?? '0.0.0';
|
|
57
|
+
// Dry run: just validate and return summary
|
|
58
|
+
if (dryRun) {
|
|
59
|
+
const lines = [
|
|
60
|
+
`## Dry Run for ${skillName}@${skillVersion}`,
|
|
61
|
+
'',
|
|
62
|
+
'**Validation:** ✅ PASSED',
|
|
63
|
+
'',
|
|
64
|
+
'### Package Summary',
|
|
65
|
+
`- **Name:** ${skillName}`,
|
|
66
|
+
`- **Version:** ${skillVersion}`,
|
|
67
|
+
`- **Visibility:** ${visibility}`,
|
|
68
|
+
`- **Files:** ${packResult.fileCount}`,
|
|
69
|
+
`- **Size:** ${(packResult.totalSize / 1024).toFixed(1)}KB compressed`,
|
|
70
|
+
`- **Integrity:** ${packResult.integrity.slice(0, 20)}...`,
|
|
71
|
+
'',
|
|
72
|
+
'### Manifest',
|
|
73
|
+
`- **Description:** ${manifest.description ?? 'No description'}`,
|
|
74
|
+
`- **Permissions:** ${JSON.stringify(manifest.permissions ?? {})}`,
|
|
75
|
+
'',
|
|
76
|
+
'### Files',
|
|
77
|
+
...packResult.files.slice(0, 10).map((f) => ` - ${f}`),
|
|
78
|
+
packResult.files.length > 10 ? ` ... and ${packResult.files.length - 10} more` : '',
|
|
79
|
+
'',
|
|
80
|
+
'Ready to publish. Say "publish my skill" when you\'re ready.',
|
|
81
|
+
];
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Step 1: Start publish flow
|
|
87
|
+
const startResult = await client.fetch('/api/v1/skills', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
manifest: { ...manifest, visibility },
|
|
91
|
+
readme: packResult.readme,
|
|
92
|
+
files: packResult.files,
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
if (!startResult.ok) {
|
|
96
|
+
return {
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `Failed to start publish: ${startResult.error}`,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const { uploadUrl, versionId } = startResult.data;
|
|
106
|
+
// Step 2: Upload tarball
|
|
107
|
+
const uploadRes = await fetch(uploadUrl, {
|
|
108
|
+
method: 'PUT',
|
|
109
|
+
headers: {
|
|
110
|
+
'Content-Type': 'application/gzip',
|
|
111
|
+
},
|
|
112
|
+
body: packResult.tarball,
|
|
113
|
+
});
|
|
114
|
+
if (!uploadRes.ok) {
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: 'text',
|
|
119
|
+
text: `Failed to upload tarball: ${uploadRes.statusText}`,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// Step 3: Confirm upload
|
|
125
|
+
const confirmResult = await client.fetch('/api/v1/skills/confirm', {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
versionId,
|
|
129
|
+
integrity: packResult.integrity,
|
|
130
|
+
fileCount: packResult.fileCount,
|
|
131
|
+
tarballSize: packResult.tarball.length,
|
|
132
|
+
readme: packResult.readme,
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
if (!confirmResult.ok) {
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
type: 'text',
|
|
140
|
+
text: `Failed to confirm publish: ${confirmResult.error}`,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const confirm = confirmResult.data;
|
|
146
|
+
const score = confirm.auditScore !== null ? `${confirm.auditScore.toFixed(1)}/10` : 'pending';
|
|
147
|
+
const lines = [
|
|
148
|
+
`## Published ${confirm.name}@${confirm.version}`,
|
|
149
|
+
'',
|
|
150
|
+
`**Status:** ✅ Successfully published`,
|
|
151
|
+
`**Visibility:** ${visibility}`,
|
|
152
|
+
`**Audit Score:** ${score}`,
|
|
153
|
+
`**Scan Verdict:** ${confirm.scanVerdict ?? 'pending'}`,
|
|
154
|
+
'',
|
|
155
|
+
'### Package Details',
|
|
156
|
+
`- **Files:** ${packResult.fileCount}`,
|
|
157
|
+
`- **Size:** ${(packResult.totalSize / 1024).toFixed(1)}KB`,
|
|
158
|
+
'',
|
|
159
|
+
`View your skill: https://tankpkg.dev/skills/${confirm.name}`,
|
|
160
|
+
];
|
|
161
|
+
return {
|
|
162
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=publish-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-skill.js","sourceRoot":"","sources":["../../src/tools/publish-skill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-skill.d.ts","sourceRoot":"","sources":["../../src/tools/scan-skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgCzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgK7D"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { TankApiClient } from '../lib/api-client.js';
|
|
4
|
+
import { pack } from '../lib/packer.js';
|
|
5
|
+
import { getConfig } from '../lib/config.js';
|
|
6
|
+
export function registerScanSkillTool(server) {
|
|
7
|
+
server.tool('scan-skill', 'Scan a skill directory for security issues. Requires authentication.', {
|
|
8
|
+
directory: z.string().optional().describe('Directory to scan (default: current directory)'),
|
|
9
|
+
}, async ({ directory = '.' }) => {
|
|
10
|
+
const absDir = path.resolve(directory);
|
|
11
|
+
let client = new TankApiClient();
|
|
12
|
+
// Check auth, guide user to login if not authenticated
|
|
13
|
+
if (!client.isAuthenticated) {
|
|
14
|
+
return {
|
|
15
|
+
content: [
|
|
16
|
+
{
|
|
17
|
+
type: 'text',
|
|
18
|
+
text: 'You need to log in first. Use the login tool to authenticate with Tank.\n\nExample: "Log in to Tank"',
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// Verify auth is still valid
|
|
24
|
+
const authCheck = await client.verifyAuth();
|
|
25
|
+
if (!authCheck.valid) {
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: 'text',
|
|
30
|
+
text: 'Your session has expired. Use the login tool to authenticate again.\n\nExample: "Log in to Tank"',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Pack the skill
|
|
36
|
+
let packResult;
|
|
37
|
+
try {
|
|
38
|
+
packResult = await pack(absDir);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: `Failed to pack skill: ${err instanceof Error ? err.message : String(err)}`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const manifest = packResult.manifest;
|
|
51
|
+
const skillName = manifest.name ?? 'unknown';
|
|
52
|
+
const skillVersion = manifest.version ?? '0.0.0';
|
|
53
|
+
// Upload tarball and trigger scan
|
|
54
|
+
const formData = new FormData();
|
|
55
|
+
const blob = new Blob([packResult.tarball], { type: 'application/gzip' });
|
|
56
|
+
formData.append('tarball', blob, `${skillName}-${skillVersion}.tgz`);
|
|
57
|
+
formData.append('manifest', JSON.stringify(manifest));
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
const scanRes = await fetch(`${config.registry}/api/v1/scan`, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
Authorization: `Bearer ${client.token}`,
|
|
63
|
+
},
|
|
64
|
+
body: formData,
|
|
65
|
+
});
|
|
66
|
+
if (!scanRes.ok) {
|
|
67
|
+
const body = await scanRes.json().catch(() => ({}));
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `Scan failed: ${body.error ?? scanRes.statusText}`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const scanResult = (await scanRes.json());
|
|
78
|
+
// Format report
|
|
79
|
+
const verdictEmoji = {
|
|
80
|
+
pass: '✅',
|
|
81
|
+
pass_with_notes: '⚠️',
|
|
82
|
+
flagged: '🚩',
|
|
83
|
+
fail: '❌',
|
|
84
|
+
};
|
|
85
|
+
const severityEmoji = {
|
|
86
|
+
critical: '🔴',
|
|
87
|
+
high: '🟠',
|
|
88
|
+
medium: '🟡',
|
|
89
|
+
low: '🟢',
|
|
90
|
+
};
|
|
91
|
+
const lines = [
|
|
92
|
+
`## Scan Results for ${skillName}@${skillVersion}`,
|
|
93
|
+
'',
|
|
94
|
+
`**Verdict:** ${verdictEmoji[scanResult.verdict] ?? ''} ${scanResult.verdict.toUpperCase()}`,
|
|
95
|
+
`**Score:** ${scanResult.audit_score.toFixed(1)}/10`,
|
|
96
|
+
`**Duration:** ${(scanResult.duration_ms / 1000).toFixed(1)}s`,
|
|
97
|
+
`**Files:** ${packResult.fileCount} (${(packResult.totalSize / 1024).toFixed(1)}KB)`,
|
|
98
|
+
'',
|
|
99
|
+
];
|
|
100
|
+
if (scanResult.findings.length > 0) {
|
|
101
|
+
lines.push(`### Findings (${scanResult.findings.length})`);
|
|
102
|
+
lines.push('');
|
|
103
|
+
// Group by severity
|
|
104
|
+
const bySeverity = {
|
|
105
|
+
critical: [],
|
|
106
|
+
high: [],
|
|
107
|
+
medium: [],
|
|
108
|
+
low: [],
|
|
109
|
+
};
|
|
110
|
+
for (const f of scanResult.findings) {
|
|
111
|
+
bySeverity[f.severity].push(f);
|
|
112
|
+
}
|
|
113
|
+
for (const severity of ['critical', 'high', 'medium', 'low']) {
|
|
114
|
+
const findings = bySeverity[severity];
|
|
115
|
+
if (findings.length === 0)
|
|
116
|
+
continue;
|
|
117
|
+
lines.push(`#### ${severityEmoji[severity]} ${severity.toUpperCase()} (${findings.length})`);
|
|
118
|
+
for (const f of findings) {
|
|
119
|
+
lines.push(`- **${f.type}**: ${f.description}`);
|
|
120
|
+
if (f.location)
|
|
121
|
+
lines.push(` - Location: ${f.location}`);
|
|
122
|
+
}
|
|
123
|
+
lines.push('');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
lines.push('No findings. Your skill looks secure!');
|
|
128
|
+
lines.push('');
|
|
129
|
+
}
|
|
130
|
+
// Stages run
|
|
131
|
+
if (scanResult.stage_results?.length > 0) {
|
|
132
|
+
lines.push('### Scan Stages');
|
|
133
|
+
lines.push('');
|
|
134
|
+
for (const stage of scanResult.stage_results) {
|
|
135
|
+
const status = stage.status === 'passed' ? '✓' : '✗';
|
|
136
|
+
lines.push(`- ${status} ${stage.stage} (${stage.duration_ms}ms)`);
|
|
137
|
+
}
|
|
138
|
+
lines.push('');
|
|
139
|
+
}
|
|
140
|
+
if (scanResult.scan_id) {
|
|
141
|
+
lines.push(`View full report: https://tankpkg.dev/scans/${scanResult.scan_id}`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=scan-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-skill.js","sourceRoot":"","sources":["../../src/tools/scan-skill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-skills.d.ts","sourceRoot":"","sources":["../../src/tools/search-skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAkBzE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgEhE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TankApiClient } from '../lib/api-client.js';
|
|
3
|
+
export function registerSearchSkillsTool(server) {
|
|
4
|
+
const client = new TankApiClient();
|
|
5
|
+
server.tool('search-skills', 'Search the Tank registry for AI agent skills', {
|
|
6
|
+
query: z.string().min(1).describe('Search query (skill name or keywords)'),
|
|
7
|
+
limit: z.number().min(1).max(50).optional().default(10).describe('Maximum results to return'),
|
|
8
|
+
}, async ({ query, limit }) => {
|
|
9
|
+
const result = await client.fetch(`/api/v1/search?q=${encodeURIComponent(query)}&limit=${limit}`);
|
|
10
|
+
if (!result.ok) {
|
|
11
|
+
return {
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: `Search failed: ${result.error}`,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const { results, total } = result.data;
|
|
21
|
+
if (results.length === 0) {
|
|
22
|
+
return {
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: 'text',
|
|
26
|
+
text: `No skills found matching "${query}". Try different keywords or browse the registry at https://tankpkg.dev`,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Format as markdown table
|
|
32
|
+
const header = '| Skill | Score | Downloads | Description |\n|-------|-------|-----------|-------------|';
|
|
33
|
+
const rows = results.map((skill) => {
|
|
34
|
+
const score = skill.auditScore !== null ? skill.auditScore.toFixed(1) : '-';
|
|
35
|
+
const downloads = skill.downloads > 1000
|
|
36
|
+
? `${(skill.downloads / 1000).toFixed(1)}k`
|
|
37
|
+
: skill.downloads.toString();
|
|
38
|
+
const desc = skill.description?.slice(0, 50) ?? 'No description';
|
|
39
|
+
return `| ${skill.name} | ${score} | ${downloads} | ${desc} |`;
|
|
40
|
+
});
|
|
41
|
+
const text = [
|
|
42
|
+
`Found ${total} skill${total !== 1 ? 's' : ''} matching "${query}":`,
|
|
43
|
+
'',
|
|
44
|
+
header,
|
|
45
|
+
...rows,
|
|
46
|
+
'',
|
|
47
|
+
'View full results: https://tankpkg.dev/search?q=' + encodeURIComponent(query),
|
|
48
|
+
].join('\n');
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: 'text', text }],
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=search-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-skills.js","sourceRoot":"","sources":["../../src/tools/search-skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAgBrD,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;IAEnC,MAAM,CAAC,IAAI,CACT,eAAe,EACf,8CAA8C,EAC9C;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QAC1E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,2BAA2B,CAAC;KAC9F,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B,oBAAoB,kBAAkB,CAAC,KAAK,CAAC,UAAU,KAAK,EAAE,CAC/D,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,kBAAkB,MAAM,CAAC,KAAK,EAAE;qBACvC;iBACF;aACF,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;QAEvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,6BAA6B,KAAK,yEAAyE;qBAClH;iBACF;aACF,CAAC;QACJ,CAAC;QAED,2BAA2B;QAC3B,MAAM,MAAM,GAAG,0FAA0F,CAAC;QAC1G,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACjC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC5E,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI;gBACtC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;gBAC3C,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,gBAAgB,CAAC;YACjE,OAAO,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,MAAM,SAAS,MAAM,IAAI,IAAI,CAAC;QACjE,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG;YACX,SAAS,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI;YACpE,EAAE;YACF,MAAM;YACN,GAAG,IAAI;YACP,EAAE;YACF,kDAAkD,GAAG,kBAAkB,CAAC,KAAK,CAAC;SAC/E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;SAC3C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-info.d.ts","sourceRoot":"","sources":["../../src/tools/skill-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2BzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkG7D"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TankApiClient } from '../lib/api-client.js';
|
|
3
|
+
export function registerSkillInfoTool(server) {
|
|
4
|
+
const client = new TankApiClient();
|
|
5
|
+
server.tool('skill-info', 'Get detailed information about a specific skill from the Tank registry', {
|
|
6
|
+
name: z.string().describe('Skill name (e.g., @org/skill-name or skill-name)'),
|
|
7
|
+
}, async ({ name }) => {
|
|
8
|
+
const result = await client.fetch(`/api/v1/skills/${encodeURIComponent(name)}`);
|
|
9
|
+
if (!result.ok) {
|
|
10
|
+
if (result.status === 404) {
|
|
11
|
+
return {
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: `Skill "${name}" not found. Search for skills: https://tankpkg.dev/search`,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: 'text',
|
|
24
|
+
text: `Failed to get skill info: ${result.error}`,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const skill = result.data;
|
|
30
|
+
const score = skill.auditScore !== null ? `${skill.auditScore.toFixed(1)}/10` : 'Not scored';
|
|
31
|
+
const size = skill.versions[0]
|
|
32
|
+
? `${(skill.versions[0].tarballSize / 1024).toFixed(1)}KB`
|
|
33
|
+
: 'Unknown';
|
|
34
|
+
// Format permissions as readable text
|
|
35
|
+
let permsText = 'None declared';
|
|
36
|
+
if (skill.permissions) {
|
|
37
|
+
const perms = [];
|
|
38
|
+
const p = skill.permissions;
|
|
39
|
+
if (p.network?.outbound?.length) {
|
|
40
|
+
perms.push(`network: ${p.network.outbound.join(', ')}`);
|
|
41
|
+
}
|
|
42
|
+
if (p.filesystem?.read?.length || p.filesystem?.write?.length) {
|
|
43
|
+
const fsPerms = [];
|
|
44
|
+
if (p.filesystem.read?.length)
|
|
45
|
+
fsPerms.push(`read: ${p.filesystem.read.length} paths`);
|
|
46
|
+
if (p.filesystem.write?.length)
|
|
47
|
+
fsPerms.push(`write: ${p.filesystem.write.length} paths`);
|
|
48
|
+
perms.push(`filesystem (${fsPerms.join(', ')})`);
|
|
49
|
+
}
|
|
50
|
+
if (p.subprocess)
|
|
51
|
+
perms.push('subprocess: allowed');
|
|
52
|
+
if (perms.length > 0)
|
|
53
|
+
permsText = perms.join('\n - ');
|
|
54
|
+
}
|
|
55
|
+
const versionsList = skill.versions
|
|
56
|
+
.slice(0, 5)
|
|
57
|
+
.map((v) => {
|
|
58
|
+
const vScore = v.auditScore !== null ? v.auditScore.toFixed(1) : '-';
|
|
59
|
+
return `${v.version} (score: ${vScore})`;
|
|
60
|
+
})
|
|
61
|
+
.join('\n - ');
|
|
62
|
+
const text = [
|
|
63
|
+
`# ${skill.name}`,
|
|
64
|
+
'',
|
|
65
|
+
`**Publisher:** ${skill.publisher}`,
|
|
66
|
+
`**Latest:** ${skill.latestVersion}`,
|
|
67
|
+
`**Score:** ${score}`,
|
|
68
|
+
`**Size:** ${size}`,
|
|
69
|
+
`**Downloads:** ${skill.downloads}`,
|
|
70
|
+
'',
|
|
71
|
+
'**Description:**',
|
|
72
|
+
skill.description ?? 'No description available',
|
|
73
|
+
'',
|
|
74
|
+
'**Permissions:**',
|
|
75
|
+
` - ${permsText}`,
|
|
76
|
+
'',
|
|
77
|
+
'**Versions:**',
|
|
78
|
+
` - ${versionsList}`,
|
|
79
|
+
skill.versions.length > 5 ? `\n ... and ${skill.versions.length - 5} more` : '',
|
|
80
|
+
'',
|
|
81
|
+
`View on Tank: https://tankpkg.dev/skills/${skill.name}`,
|
|
82
|
+
].join('\n');
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: 'text', text }],
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=skill-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-info.js","sourceRoot":"","sources":["../../src/tools/skill-info.ts"],"names":[],"mappings":"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"}
|