@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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +30 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/api-client.d.ts +44 -0
  8. package/dist/lib/api-client.d.ts.map +1 -0
  9. package/dist/lib/api-client.js +78 -0
  10. package/dist/lib/api-client.js.map +1 -0
  11. package/dist/lib/config.d.ts +25 -0
  12. package/dist/lib/config.d.ts.map +1 -0
  13. package/dist/lib/config.js +59 -0
  14. package/dist/lib/config.js.map +1 -0
  15. package/dist/lib/packer.d.ts +14 -0
  16. package/dist/lib/packer.d.ts.map +1 -0
  17. package/dist/lib/packer.js +191 -0
  18. package/dist/lib/packer.js.map +1 -0
  19. package/dist/tools/login.d.ts +3 -0
  20. package/dist/tools/login.d.ts.map +1 -0
  21. package/dist/tools/login.js +104 -0
  22. package/dist/tools/login.js.map +1 -0
  23. package/dist/tools/publish-skill.d.ts +3 -0
  24. package/dist/tools/publish-skill.d.ts.map +1 -0
  25. package/dist/tools/publish-skill.js +166 -0
  26. package/dist/tools/publish-skill.js.map +1 -0
  27. package/dist/tools/scan-skill.d.ts +3 -0
  28. package/dist/tools/scan-skill.d.ts.map +1 -0
  29. package/dist/tools/scan-skill.js +148 -0
  30. package/dist/tools/scan-skill.js.map +1 -0
  31. package/dist/tools/search-skills.d.ts +3 -0
  32. package/dist/tools/search-skills.d.ts.map +1 -0
  33. package/dist/tools/search-skills.js +54 -0
  34. package/dist/tools/search-skills.js.map +1 -0
  35. package/dist/tools/skill-info.d.ts +3 -0
  36. package/dist/tools/skill-info.d.ts.map +1 -0
  37. package/dist/tools/skill-info.js +88 -0
  38. package/dist/tools/skill-info.js.map +1 -0
  39. 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,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerPublishSkillTool(server: McpServer): void;
3
+ //# sourceMappingURL=publish-skill.d.ts.map
@@ -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,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerScanSkillTool(server: McpServer): void;
3
+ //# sourceMappingURL=scan-skill.d.ts.map
@@ -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,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerSearchSkillsTool(server: McpServer): void;
3
+ //# sourceMappingURL=search-skills.d.ts.map
@@ -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,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerSkillInfoTool(server: McpServer): void;
3
+ //# sourceMappingURL=skill-info.d.ts.map
@@ -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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"}