@tankpkg/mcp-server 0.6.3 → 0.8.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.
Files changed (87) hide show
  1. package/README.md +11 -4
  2. package/dist/index.d.ts +1 -3
  3. package/dist/index.js +2215 -29
  4. package/dist/index.js.map +1 -1
  5. package/package.json +20 -14
  6. package/LICENSE +0 -21
  7. package/dist/index.d.ts.map +0 -1
  8. package/dist/lib/api-client.d.ts +0 -45
  9. package/dist/lib/api-client.d.ts.map +0 -1
  10. package/dist/lib/api-client.js +0 -78
  11. package/dist/lib/api-client.js.map +0 -1
  12. package/dist/lib/config.d.ts +0 -25
  13. package/dist/lib/config.d.ts.map +0 -1
  14. package/dist/lib/config.js +0 -59
  15. package/dist/lib/config.js.map +0 -1
  16. package/dist/lib/packer.d.ts +0 -34
  17. package/dist/lib/packer.d.ts.map +0 -1
  18. package/dist/lib/packer.js +0 -276
  19. package/dist/lib/packer.js.map +0 -1
  20. package/dist/tools/audit-skill.d.ts +0 -3
  21. package/dist/tools/audit-skill.d.ts.map +0 -1
  22. package/dist/tools/audit-skill.js +0 -213
  23. package/dist/tools/audit-skill.js.map +0 -1
  24. package/dist/tools/doctor.d.ts +0 -3
  25. package/dist/tools/doctor.d.ts.map +0 -1
  26. package/dist/tools/doctor.js +0 -158
  27. package/dist/tools/doctor.js.map +0 -1
  28. package/dist/tools/init-skill.d.ts +0 -3
  29. package/dist/tools/init-skill.d.ts.map +0 -1
  30. package/dist/tools/init-skill.js +0 -72
  31. package/dist/tools/init-skill.js.map +0 -1
  32. package/dist/tools/install-skill.d.ts +0 -3
  33. package/dist/tools/install-skill.d.ts.map +0 -1
  34. package/dist/tools/install-skill.js +0 -206
  35. package/dist/tools/install-skill.js.map +0 -1
  36. package/dist/tools/link-skill.d.ts +0 -3
  37. package/dist/tools/link-skill.d.ts.map +0 -1
  38. package/dist/tools/link-skill.js +0 -81
  39. package/dist/tools/link-skill.js.map +0 -1
  40. package/dist/tools/login.d.ts +0 -3
  41. package/dist/tools/login.d.ts.map +0 -1
  42. package/dist/tools/login.js +0 -104
  43. package/dist/tools/login.js.map +0 -1
  44. package/dist/tools/logout.d.ts +0 -3
  45. package/dist/tools/logout.d.ts.map +0 -1
  46. package/dist/tools/logout.js +0 -19
  47. package/dist/tools/logout.js.map +0 -1
  48. package/dist/tools/publish-skill.d.ts +0 -3
  49. package/dist/tools/publish-skill.d.ts.map +0 -1
  50. package/dist/tools/publish-skill.js +0 -166
  51. package/dist/tools/publish-skill.js.map +0 -1
  52. package/dist/tools/remove-skill.d.ts +0 -3
  53. package/dist/tools/remove-skill.d.ts.map +0 -1
  54. package/dist/tools/remove-skill.js +0 -110
  55. package/dist/tools/remove-skill.js.map +0 -1
  56. package/dist/tools/scan-skill.d.ts +0 -3
  57. package/dist/tools/scan-skill.d.ts.map +0 -1
  58. package/dist/tools/scan-skill.js +0 -200
  59. package/dist/tools/scan-skill.js.map +0 -1
  60. package/dist/tools/search-skills.d.ts +0 -3
  61. package/dist/tools/search-skills.d.ts.map +0 -1
  62. package/dist/tools/search-skills.js +0 -54
  63. package/dist/tools/search-skills.js.map +0 -1
  64. package/dist/tools/skill-info.d.ts +0 -3
  65. package/dist/tools/skill-info.d.ts.map +0 -1
  66. package/dist/tools/skill-info.js +0 -88
  67. package/dist/tools/skill-info.js.map +0 -1
  68. package/dist/tools/skill-permissions.d.ts +0 -3
  69. package/dist/tools/skill-permissions.d.ts.map +0 -1
  70. package/dist/tools/skill-permissions.js +0 -311
  71. package/dist/tools/skill-permissions.js.map +0 -1
  72. package/dist/tools/unlink-skill.d.ts +0 -3
  73. package/dist/tools/unlink-skill.d.ts.map +0 -1
  74. package/dist/tools/unlink-skill.js +0 -72
  75. package/dist/tools/unlink-skill.js.map +0 -1
  76. package/dist/tools/update-skill.d.ts +0 -3
  77. package/dist/tools/update-skill.d.ts.map +0 -1
  78. package/dist/tools/update-skill.js +0 -317
  79. package/dist/tools/update-skill.js.map +0 -1
  80. package/dist/tools/verify-skills.d.ts +0 -3
  81. package/dist/tools/verify-skills.d.ts.map +0 -1
  82. package/dist/tools/verify-skills.js +0 -121
  83. package/dist/tools/verify-skills.js.map +0 -1
  84. package/dist/tools/whoami.d.ts +0 -3
  85. package/dist/tools/whoami.d.ts.map +0 -1
  86. package/dist/tools/whoami.js +0 -29
  87. package/dist/tools/whoami.js.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,6BAA6B;AAC7B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,qBAAqB;AACrB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,4BAA4B,CAAC,MAAM,CAAC,CAAC;AACrC,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAE/B,wBAAwB;AACxB,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","names":["MAX_PACKAGE_SIZE","SCOPED_NAME_PATTERN","parseLockKey","SCOPED_NAME_PATTERN","SCOPED_NAME_PATTERN","getSkillDir","crypto","SCOPED_NAME_PATTERN","getSkillDir","crypto","SCOPED_NAME_PATTERN","getSkillDir","SCOPED_NAME_PATTERN","getSkillDir","parseLockKey"],"sources":["../../shared/dist/index.js","../src/lib/config.ts","../src/lib/api-client.ts","../src/tools/audit-skill.ts","../src/tools/doctor.ts","../src/tools/init-skill.ts","../src/tools/install-skill.ts","../src/tools/link-skill.ts","../src/tools/login.ts","../src/tools/logout.ts","../src/lib/packer.ts","../src/tools/publish-skill.ts","../src/tools/remove-skill.ts","../src/tools/scan-skill.ts","../src/tools/search-skills.ts","../src/tools/skill-info.ts","../src/tools/skill-permissions.ts","../src/tools/unlink-skill.ts","../src/tools/update-skill.ts","../src/tools/verify-skills.ts","../src/tools/whoami.ts","../src/index.ts"],"sourcesContent":["import semver from \"semver\";\nimport { z } from \"zod\";\n//#region src/brand.ts\n/**\n* Default Tank brand configuration\n* Used when no custom brand is configured\n*/\nconst DEFAULT_BRAND = {\n\tname: \"Tank\",\n\ttagline: \"Security-first package manager for AI agent skills\",\n\turl: \"https://tankpkg.dev\",\n\tlogo: {\n\t\tdefault: \"/logo.png\",\n\t\ttight: \"/logo-tight.png\"\n\t},\n\tfavicon: \"/favicon.ico\",\n\togImage: \"/og-default.png\",\n\tcolors: {\n\t\tprimary: \"10b981\",\n\t\tsecondary: \"3b82f6\",\n\t\taccent: \"f59e0b\",\n\t\tbackground: \"0f172a\"\n\t},\n\tdarkColors: {\n\t\tprimary: \"10b981\",\n\t\tsecondary: \"3b82f6\",\n\t\taccent: \"f59e0b\",\n\t\tbackground: \"0f172a\"\n\t},\n\tsocial: {\n\t\ttwitter: \"@tankpkg\",\n\t\tgithub: \"tankpkg/tank\"\n\t}\n};\n/**\n* Hex color validation regex\n*/\nconst HEX_COLOR_REGEX = /^[0-9a-fA-F]{6}$/;\n/**\n* Validates a hex color string (without # prefix)\n*/\nfunction isValidHexColor(color) {\n\treturn HEX_COLOR_REGEX.test(color);\n}\n/**\n* Converts hex color to oklch format for CSS\n* Uses a simplified conversion that maintains hue\n*/\nfunction hexToOklch(hex) {\n\treturn `#${hex}`;\n}\n//#endregion\n//#region src/constants/permissions.ts\nconst PERMISSION_CATEGORIES = [\n\t\"network\",\n\t\"filesystem\",\n\t\"subprocess\"\n];\nconst DEFAULT_PERMISSIONS = {\n\tnetwork: void 0,\n\tfilesystem: void 0,\n\tsubprocess: false\n};\n//#endregion\n//#region src/constants/registry.ts\nconst REGISTRY_URL = \"https://tankpkg.dev\";\nconst REGISTRY_API_VERSION = \"v1\";\nconst MAX_PACKAGE_SIZE = 50 * 1024 * 1024;\nconst MAX_FILE_COUNT = 1e3;\nconst MAX_NAME_LENGTH = 214;\nconst MAX_DESCRIPTION_LENGTH = 500;\nconst LOCKFILE_VERSION = 2;\nconst MANIFEST_FILENAME = \"tank.json\";\nconst LEGACY_MANIFEST_FILENAME = \"skills.json\";\nconst LOCKFILE_FILENAME = \"tank.lock\";\nconst LEGACY_LOCKFILE_FILENAME = \"skills.lock\";\n//#endregion\n//#region src/lib/resolver.ts\n/**\n* Resolves a semver range against a list of available versions.\n* Returns the highest version that satisfies the range, or null if none match.\n*\n* Pre-release versions are excluded from range matching unless the range\n* explicitly includes a pre-release tag (e.g., \">=1.0.0-beta.1\").\n* Exact version matches always work, including for pre-release versions.\n*\n* @param range - A semver range string (e.g., \"^2.1.0\", \"~1.0.0\", \">=2.0.0 <3.0.0\", \"*\")\n* @param versions - An array of semver version strings to match against\n* @returns The highest matching version string, or null if no match\n*/\nfunction resolve(range, versions) {\n\ttry {\n\t\tif (!range || !semver.validRange(range)) return null;\n\t\tconst validVersions = versions.filter((v) => semver.valid(v) !== null);\n\t\tif (validVersions.length === 0) return null;\n\t\treturn semver.maxSatisfying(validVersions, range) ?? null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n/**\n* Sorts an array of semver version strings in descending order (newest first).\n* Invalid version strings are filtered out.\n* Does not mutate the original array.\n*\n* @param versions - An array of semver version strings\n* @returns A new array sorted descending by semver precedence\n*/\nfunction sortVersions(versions) {\n\treturn [...versions.filter((v) => semver.valid(v) !== null)].sort((a, b) => semver.rcompare(a, b));\n}\n//#endregion\n//#region src/lib/url.ts\n/**\n* Encode a skill/package name for use in URLs.\n*\n* Like `encodeURIComponent` but keeps `@` and `/` unencoded so scoped\n* package names produce clean, human-readable paths:\n*\n* encodeSkillName('@tank/bulletproof') → '@tank/bulletproof'\n* encodeSkillName('my skill') → 'my%20skill'\n*/\nfunction encodeSkillName(name) {\n\treturn encodeURIComponent(name).replace(/%40/g, \"@\").replace(/%2F/gi, \"/\");\n}\n//#endregion\n//#region src/schemas/permissions.ts\nconst networkPermissionsSchema = z.object({ outbound: z.array(z.string()).optional() }).strict();\nconst filesystemPermissionsSchema = z.object({\n\tread: z.array(z.string()).optional(),\n\twrite: z.array(z.string()).optional()\n}).strict();\nconst permissionsSchema = z.object({\n\tnetwork: networkPermissionsSchema.optional(),\n\tfilesystem: filesystemPermissionsSchema.optional(),\n\tsubprocess: z.boolean().optional()\n}).strict();\nconst userRoleSchema = z.enum([\"user\", \"admin\"]);\nconst userStatusSchema = z.enum([\n\t\"active\",\n\t\"suspended\",\n\t\"banned\"\n]);\nconst skillStatusSchema = z.enum([\n\t\"active\",\n\t\"deprecated\",\n\t\"quarantined\",\n\t\"removed\"\n]);\nconst adminActionSchema = z.enum([\n\t\"user.ban\",\n\t\"user.suspend\",\n\t\"user.unban\",\n\t\"user.promote\",\n\t\"user.demote\",\n\t\"skill.quarantine\",\n\t\"skill.remove\",\n\t\"skill.deprecate\",\n\t\"skill.restore\",\n\t\"skill.feature\",\n\t\"skill.unfeature\",\n\t\"org.suspend\",\n\t\"org.member.remove\",\n\t\"org.delete\"\n]);\nfunction isAdmin(role) {\n\treturn role === \"admin\";\n}\nconst skillsJsonSchema = z.object({\n\tname: z.string().min(1, \"Name must not be empty\").max(214, \"Name must be 214 characters or fewer\").regex(/^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/, \"Name must be scoped (@org/name), lowercase alphanumeric and hyphens\"),\n\tversion: z.string().regex(/^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/, \"Version must be valid semver\"),\n\tdescription: z.string().max(500, \"Description must be 500 characters or fewer\").optional(),\n\tskills: z.record(z.string(), z.string()).optional(),\n\tpermissions: permissionsSchema.optional(),\n\trepository: z.string().url(\"Repository must be a valid URL\").optional(),\n\tvisibility: z.enum([\"public\", \"private\"]).optional(),\n\taudit: z.object({ min_score: z.number().min(0).max(10) }).strict().optional()\n}).strict();\n//#endregion\n//#region src/schemas/skills-lock.ts\nconst lockedSkillV1Schema = z.object({\n\tresolved: z.string().url(),\n\tintegrity: z.string().regex(/^sha512-/, \"Integrity must start with sha512-\"),\n\tpermissions: permissionsSchema,\n\taudit_score: z.number().min(0).max(10).nullable()\n});\nconst skillsLockV1Schema = z.object({\n\tlockfileVersion: z.literal(1),\n\tskills: z.record(z.string(), lockedSkillV1Schema)\n});\nconst lockedSkillSchema = z.object({\n\tresolved: z.string().url(),\n\tintegrity: z.string().regex(/^sha512-/, \"Integrity must start with sha512-\"),\n\tpermissions: permissionsSchema,\n\taudit_score: z.number().min(0).max(10).nullable(),\n\tdependencies: z.record(z.string(), z.string()).optional()\n});\nconst skillsLockSchema = z.object({\n\tlockfileVersion: z.union([z.literal(1), z.literal(2)]),\n\tskills: z.record(z.string(), lockedSkillSchema)\n});\n//#endregion\nexport { DEFAULT_BRAND, DEFAULT_PERMISSIONS, LEGACY_LOCKFILE_FILENAME, LEGACY_MANIFEST_FILENAME, LOCKFILE_FILENAME, LOCKFILE_VERSION, MANIFEST_FILENAME, MAX_DESCRIPTION_LENGTH, MAX_FILE_COUNT, MAX_NAME_LENGTH, MAX_PACKAGE_SIZE, PERMISSION_CATEGORIES, REGISTRY_API_VERSION, REGISTRY_URL, adminActionSchema, encodeSkillName, filesystemPermissionsSchema, hexToOklch, isAdmin, isValidHexColor, networkPermissionsSchema, permissionsSchema, resolve, skillStatusSchema, skillsJsonSchema, skillsLockSchema, skillsLockV1Schema, sortVersions, userRoleSchema, userStatusSchema };\n\n//# sourceMappingURL=index.js.map","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface TankConfig {\n token?: string;\n user?: { name: string; email: string };\n registry: string;\n}\n\nconst DEFAULT_CONFIG: TankConfig = {\n registry: 'https://tankpkg.dev'\n};\n\n/**\n * Get the path to the tank config directory.\n */\nexport function getConfigDir(configDir?: string): string {\n return configDir ?? path.join(os.homedir(), '.tank');\n}\n\n/**\n * Get the path to the tank config file.\n */\nexport function getConfigPath(configDir?: string): string {\n return path.join(getConfigDir(configDir), 'config.json');\n}\n\n/**\n * Read the tank config file. Returns defaults if file doesn't exist.\n */\nexport function getConfig(configDir?: string): TankConfig {\n const configPath = getConfigPath(configDir);\n\n try {\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<TankConfig>;\n const merged = { ...DEFAULT_CONFIG, ...parsed };\n // TANK_TOKEN env var takes priority\n const envToken = process.env.TANK_TOKEN?.trim();\n if (envToken) {\n merged.token = envToken;\n }\n return merged;\n } catch {\n const envToken = process.env.TANK_TOKEN?.trim();\n return {\n ...DEFAULT_CONFIG,\n ...(envToken ? { token: envToken } : {})\n };\n }\n}\n\n/**\n * Write config to disk. Merges with existing config.\n */\nexport function setConfig(partial: Partial<TankConfig>, configDir?: string): void {\n const dir = getConfigDir(configDir);\n const configPath = getConfigPath(configDir);\n\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const existing = getConfig(configDir);\n const merged = { ...existing, ...partial };\n\n fs.writeFileSync(configPath, `${JSON.stringify(merged, null, 2)}\\n`, {\n encoding: 'utf-8',\n mode: 0o600\n });\n}\n","import { getConfig, type TankConfig } from './config.js';\n\nexport interface ApiClientOptions {\n configDir?: string;\n}\n\n/**\n * Tank API client for MCP server.\n */\nexport class TankApiClient {\n private config: TankConfig;\n\n constructor(options: ApiClientOptions = {}) {\n this.config = getConfig(options.configDir);\n }\n\n /**\n * Get the base URL for the Tank API.\n */\n get baseUrl(): string {\n return this.config.registry;\n }\n\n /**\n * Get the auth token (if available).\n */\n get token(): string | undefined {\n return this.config.token;\n }\n\n /**\n * Check if authenticated.\n */\n get isAuthenticated(): boolean {\n return !!this.config.token;\n }\n\n /**\n * Make an authenticated API request.\n */\n async fetch<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<{ data: T; ok: true } | { error: string; status: number; ok: false }> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string>)\n };\n\n if (this.config.token) {\n headers.Authorization = `Bearer ${this.config.token}`;\n }\n\n try {\n const response = await fetch(url, {\n ...options,\n headers\n });\n\n if (!response.ok) {\n const body = await response.json().catch(() => ({}));\n return {\n error: (body as { error?: string }).error ?? response.statusText,\n status: response.status,\n ok: false\n };\n }\n\n const data = (await response.json()) as T;\n return { data, ok: true };\n } catch (err) {\n return {\n error: err instanceof Error ? err.message : 'Network error',\n status: 0,\n ok: false\n };\n }\n }\n\n async verifyAuth(): Promise<\n | { valid: true; user: { name: string | null; email: string | null } }\n | { valid: false; reason: 'no-token' | 'unauthorized' | 'network-error'; error?: string }\n > {\n if (!this.config.token) {\n return { valid: false, reason: 'no-token' };\n }\n\n const result = await this.fetch<{ name: string | null; email: string | null; userId: string }>(\n '/api/v1/auth/whoami'\n );\n\n if (result.ok) {\n return { valid: true, user: { name: result.data.name, email: result.data.email } };\n }\n\n if (result.status === 0) {\n return { valid: false, reason: 'network-error', error: result.error };\n }\n\n return { valid: false, reason: 'unauthorized' };\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { LEGACY_LOCKFILE_FILENAME, LOCKFILE_FILENAME, type SkillsLock } from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\n\ninterface VersionDetails {\n name: string;\n version: string;\n permissions?: {\n network?: { outbound?: string[] };\n filesystem?: { read?: string[]; write?: string[] };\n subprocess?: boolean;\n };\n auditScore: number | null;\n auditStatus: string;\n downloadUrl: string;\n publishedAt: string;\n downloads: number;\n}\n\ninterface ScanFinding {\n stage: string;\n severity: 'critical' | 'high' | 'medium' | 'low';\n type: string;\n description: string;\n location: string | null;\n}\n\ninterface ScanResult {\n verdict: string;\n audit_score: number;\n findings: ScanFinding[];\n scanned_at: string;\n}\n\ninterface SkillMetaResponse {\n name: string;\n description: string | null;\n latestVersion: string;\n auditScore: number | null;\n auditStatus: string;\n versions: Array<{\n version: string;\n auditScore: number | null;\n auditStatus: string;\n publishedAt: string;\n }>;\n}\n\nfunction parseLockKey(key: string): { name: string; version: string } | null {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) return null;\n return { name: key.slice(0, lastAt), version: key.slice(lastAt + 1) };\n}\n\nfunction deriveVerdict(score: number | null, status: string): string {\n if (status !== 'completed' || score === null) return 'PENDING';\n if (score >= 7) return 'PASS';\n if (score >= 4) return 'FLAGGED';\n return 'FAIL';\n}\n\nfunction formatFindings(findings: ScanFinding[]): string {\n if (findings.length === 0) return '';\n\n const bySeverity: Record<string, ScanFinding[]> = {\n critical: [],\n high: [],\n medium: [],\n low: []\n };\n\n for (const f of findings) {\n if (bySeverity[f.severity]) {\n bySeverity[f.severity].push(f);\n }\n }\n\n const lines: string[] = ['', `### Findings (${findings.length})`];\n\n for (const severity of ['critical', 'high', 'medium', 'low'] as const) {\n const group = bySeverity[severity];\n if (group.length === 0) continue;\n lines.push(`\\n**${severity.toUpperCase()} (${group.length}):**`);\n for (const f of group) {\n lines.push(`- ${f.type}: ${f.description}${f.location ? ` (${f.location})` : ''}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nexport function registerAuditSkillTool(server: McpServer): void {\n server.tool(\n 'audit-skill',\n 'Show security audit results for a skill from the Tank registry.',\n {\n name: z.string().describe('Skill name in @org/name format'),\n version: z.string().optional().describe('Specific version to audit (defaults to installed or latest)')\n },\n async ({ name, version }) => {\n if (!SCOPED_NAME_PATTERN.test(name)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Validation error: Skill name \"${name}\" must use the @org/name format (e.g. @acme/my-skill).`\n }\n ],\n isError: true\n };\n }\n\n const client = new TankApiClient();\n if (!client.isAuthenticated) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Authentication required. Please run the \"login\" tool first to authenticate with Tank.'\n }\n ],\n isError: true\n };\n }\n\n const encodedName = encodeURIComponent(name);\n\n // If no version specified, try to find installed version from lockfile\n let targetVersion = version;\n if (!targetVersion) {\n let lockPath = path.join(process.cwd(), LOCKFILE_FILENAME);\n if (!fs.existsSync(lockPath)) {\n lockPath = path.join(process.cwd(), LEGACY_LOCKFILE_FILENAME);\n }\n if (fs.existsSync(lockPath)) {\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n const lock = JSON.parse(raw) as SkillsLock;\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (parsed && parsed.name === name) {\n targetVersion = parsed.version;\n break;\n }\n }\n } catch {\n // Lockfile unreadable — fall through to fetch latest\n }\n }\n }\n\n // If still no version, fetch skill metadata to get latest\n if (!targetVersion) {\n const metaResult = await client.fetch<SkillMetaResponse>(`/api/v1/skills/${encodedName}`);\n\n if (!metaResult.ok) {\n if (metaResult.status === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Unable to connect to the Tank registry. Check your network connection and try again.'\n }\n ],\n isError: true\n };\n }\n if (metaResult.status === 404) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" not found in the Tank registry.`\n }\n ],\n isError: true\n };\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to fetch skill metadata: ${metaResult.error}`\n }\n ],\n isError: true\n };\n }\n\n targetVersion = metaResult.data.latestVersion;\n }\n\n // Fetch version details with audit data\n const versionResult = await client.fetch<VersionDetails>(`/api/v1/skills/${encodedName}/${targetVersion}`);\n\n if (!versionResult.ok) {\n if (versionResult.status === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Unable to connect to the Tank registry. Check your network connection and try again.'\n }\n ],\n isError: true\n };\n }\n if (versionResult.status === 404) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" version \"${targetVersion}\" not found in the Tank registry.`\n }\n ],\n isError: true\n };\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to fetch audit data: ${versionResult.error}`\n }\n ],\n isError: true\n };\n }\n\n const details = versionResult.data;\n const verdict = deriveVerdict(details.auditScore, details.auditStatus);\n\n if (details.auditStatus !== 'completed') {\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n `## Audit: ${name}@${targetVersion}`,\n '',\n `**Status:** Pending security review`,\n `**Scan Status:** ${details.auditStatus}`,\n '',\n 'This skill has not yet been through security scanning. Results will be available once the scan completes.'\n ].join('\\n')\n }\n ]\n };\n }\n\n // Try to fetch detailed scan results\n let findingsText = '';\n const scanResult = await client.fetch<ScanResult>(`/api/v1/skills/${encodedName}/${targetVersion}/scan`);\n if (scanResult.ok && scanResult.data.findings) {\n findingsText = formatFindings(scanResult.data.findings);\n }\n\n const score = details.auditScore !== null ? details.auditScore.toFixed(1) : 'N/A';\n\n const lines = [\n `## Audit: ${name}@${targetVersion}`,\n '',\n `**Verdict:** ${verdict}`,\n `**Score:** ${score}/10`,\n `**Scanned:** ${details.publishedAt}`,\n `**Version:** ${targetVersion}`\n ];\n\n if (details.permissions) {\n lines.push('', '**Permissions:**');\n const p = details.permissions;\n if (p.network?.outbound?.length) {\n lines.push(` - Network: ${p.network.outbound.join(', ')}`);\n }\n if (p.filesystem?.read?.length || p.filesystem?.write?.length) {\n const parts: string[] = [];\n if (p.filesystem.read?.length) parts.push(`read: ${p.filesystem.read.join(', ')}`);\n if (p.filesystem.write?.length) parts.push(`write: ${p.filesystem.write.join(', ')}`);\n lines.push(` - Filesystem: ${parts.join('; ')}`);\n }\n lines.push(` - Subprocess: ${p.subprocess ? 'yes' : 'no'}`);\n }\n\n if (findingsText) {\n lines.push(findingsText);\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: lines.join('\\n')\n }\n ]\n };\n }\n );\n}\n","import fs from 'node:fs';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { TankApiClient } from '../lib/api-client.js';\nimport { getConfig, getConfigPath } from '../lib/config.js';\n\nconst MIN_NODE_MAJOR = 24;\n\ninterface CheckResult {\n name: string;\n status: 'PASS' | 'FAIL';\n message: string;\n}\n\nfunction checkConfigFile(): CheckResult {\n const configPath = getConfigPath();\n\n if (!fs.existsSync(configPath)) {\n return {\n name: 'Configuration File',\n status: 'FAIL',\n message: `Configuration file not found at ${configPath}. Run the login tool to create it.`\n };\n }\n\n try {\n const raw = fs.readFileSync(configPath, 'utf-8');\n JSON.parse(raw);\n return {\n name: 'Configuration File',\n status: 'PASS',\n message: `Configuration file exists and is valid JSON (${configPath}).`\n };\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n return {\n name: 'Configuration File',\n status: 'FAIL',\n message: `Configuration file at ${configPath} is malformed: ${detail}`\n };\n }\n}\n\nasync function checkAuthentication(): Promise<CheckResult> {\n const client = new TankApiClient();\n\n if (!client.isAuthenticated) {\n return {\n name: 'Authentication',\n status: 'FAIL',\n message: 'Not authenticated. Use the login tool to authenticate with Tank.'\n };\n }\n\n const authCheck = await client.verifyAuth();\n\n if (authCheck.valid) {\n const name = authCheck.user.name ?? 'unknown';\n return {\n name: 'Authentication',\n status: 'PASS',\n message: `Authenticated as ${name}.`\n };\n }\n\n if (authCheck.reason === 'network-error') {\n return {\n name: 'Authentication',\n status: 'FAIL',\n message: `Could not verify credentials (network error). ${authCheck.error ?? ''}`.trim()\n };\n }\n\n return {\n name: 'Authentication',\n status: 'FAIL',\n message: 'Credentials are expired or invalid. Use the login tool to re-authenticate.'\n };\n}\n\nasync function checkRegistryConnectivity(): Promise<CheckResult> {\n const config = getConfig();\n const registryUrl = config.registry;\n\n try {\n const healthUrl = `${registryUrl}/api/health`;\n const response = await fetch(healthUrl, {\n signal: AbortSignal.timeout(10_000)\n });\n\n if (response.ok) {\n return {\n name: 'Registry Connectivity',\n status: 'PASS',\n message: `Registry at ${registryUrl} is reachable.`\n };\n }\n\n return {\n name: 'Registry Connectivity',\n status: 'FAIL',\n message: `Registry at ${registryUrl} returned HTTP ${response.status}.`\n };\n } catch {\n return {\n name: 'Registry Connectivity',\n status: 'FAIL',\n message: `Cannot reach registry at ${registryUrl}. Check your network connection.`\n };\n }\n}\n\nfunction checkNodeVersion(): CheckResult {\n const raw = process.version;\n const match = raw.match(/^v(\\d+)/);\n const major = match ? Number.parseInt(match[1], 10) : 0;\n\n if (major >= MIN_NODE_MAJOR) {\n return {\n name: 'Node.js Version',\n status: 'PASS',\n message: `Node.js ${raw} meets the minimum requirement (v${MIN_NODE_MAJOR}.0.0).`\n };\n }\n\n return {\n name: 'Node.js Version',\n status: 'FAIL',\n message: `Node.js ${raw} is below the minimum required version v${MIN_NODE_MAJOR}.0.0. Please upgrade Node.js.`\n };\n}\n\nfunction formatChecks(checks: CheckResult[]): string {\n const lines: string[] = [];\n\n lines.push('Tank Doctor Report');\n lines.push('==================');\n lines.push('');\n\n for (const check of checks) {\n const icon = check.status === 'PASS' ? 'PASS' : 'FAIL';\n lines.push(`[${icon}] ${check.name}`);\n lines.push(` ${check.message}`);\n }\n\n lines.push('');\n\n const failedChecks = checks.filter((c) => c.status === 'FAIL');\n const allPassed = failedChecks.length === 0;\n\n if (allPassed) {\n lines.push('All checks passed. Your Tank environment is ready to use.');\n } else {\n lines.push('Suggestions:');\n for (const check of failedChecks) {\n if (check.name === 'Authentication') {\n lines.push(' - Use the login tool to authenticate with Tank.');\n } else if (check.name === 'Registry Connectivity') {\n lines.push(' - Check your network connection and verify the registry URL.');\n } else if (check.name === 'Node.js Version') {\n lines.push(` - Upgrade Node.js to v${MIN_NODE_MAJOR}.0.0 or later.`);\n } else if (check.name === 'Configuration File') {\n lines.push(' - Use the login tool to create a valid configuration file.');\n }\n }\n lines.push('');\n lines.push('The environment is not healthy. Please address the issues above.');\n }\n\n return lines.join('\\n');\n}\n\nexport function registerDoctorTool(server: McpServer): void {\n server.tool('doctor', 'Diagnose Tank setup and environment.', {}, async () => {\n const checks: CheckResult[] = [];\n\n checks.push(checkConfigFile());\n checks.push(await checkAuthentication());\n checks.push(await checkRegistryConnectivity());\n checks.push(checkNodeVersion());\n\n return {\n content: [{ type: 'text' as const, text: formatChecks(checks) }]\n };\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME, type SkillsJson, skillsJsonSchema } from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\nconst SEMVER_PATTERN = /^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/;\n\nexport function registerInitSkillTool(server: McpServer): void {\n server.tool(\n 'init-skill',\n `Create a new ${MANIFEST_FILENAME} and SKILL.md template for a Tank skill.`,\n {\n name: z.string().regex(SCOPED_NAME_PATTERN, 'Name must be in @org/name format'),\n version: z.string().regex(SEMVER_PATTERN, 'Version must be valid semver').optional().default('0.1.0'),\n description: z.string().optional().default(''),\n directory: z.string().optional().default('.')\n },\n async ({ name, version = '0.1.0', description = '', directory = '.' }) => {\n const targetDir = path.resolve(directory);\n\n if (!fs.existsSync(targetDir)) {\n return {\n content: [{ type: 'text' as const, text: `Directory does not exist: ${targetDir}` }]\n };\n }\n\n if (!fs.statSync(targetDir).isDirectory()) {\n return {\n content: [{ type: 'text' as const, text: `Path is not a directory: ${targetDir}` }]\n };\n }\n\n // Check for existing manifest (tank.json or skills.json)\n const newManifestPath = path.join(targetDir, MANIFEST_FILENAME);\n const legacyManifestPath = path.join(targetDir, LEGACY_MANIFEST_FILENAME);\n const skillsJsonPath = newManifestPath;\n if (fs.existsSync(newManifestPath) || fs.existsSync(legacyManifestPath)) {\n const existingFile = fs.existsSync(newManifestPath) ? MANIFEST_FILENAME : LEGACY_MANIFEST_FILENAME;\n return {\n content: [\n {\n type: 'text' as const,\n text: `${existingFile} already exists at ${targetDir}. Aborting to avoid overwrite.`\n }\n ]\n };\n }\n\n const manifest: SkillsJson = {\n name,\n version,\n description,\n skills: {},\n permissions: {\n network: { outbound: [] },\n filesystem: { read: [], write: [] },\n subprocess: false\n }\n };\n\n const parseResult = skillsJsonSchema.safeParse(manifest);\n if (!parseResult.success) {\n const details = parseResult.error.issues\n .map((issue) => `${issue.path.join('.') || 'root'}: ${issue.message}`)\n .join('; ');\n\n return {\n content: [{ type: 'text' as const, text: `Failed to create ${MANIFEST_FILENAME}: ${details}` }]\n };\n }\n\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(manifest, null, 2)}\\n`, 'utf-8');\n\n const skillMdPath = path.join(targetDir, 'SKILL.md');\n let createdSkillMd = false;\n if (!fs.existsSync(skillMdPath)) {\n const skillMd = `# ${name}\\n\\n${description || 'Description here.'}\\n`;\n fs.writeFileSync(skillMdPath, skillMd, 'utf-8');\n createdSkillMd = true;\n }\n\n const skillMdLine = createdSkillMd ? `Created: ${skillMdPath}` : `Skipped existing: ${skillMdPath}`;\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Initialized skill in ${targetDir}\\nCreated: ${skillsJsonPath}\\n${skillMdLine}`\n }\n ]\n };\n }\n );\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n LOCKFILE_VERSION,\n MANIFEST_FILENAME,\n type Permissions,\n resolve,\n type SkillsLock\n} from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { extract } from 'tar';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\n\ninterface VersionInfo {\n version: string;\n integrity: string;\n auditScore: number;\n auditStatus: string;\n publishedAt: string;\n}\n\ninterface VersionMetadata {\n name: string;\n version: string;\n description?: string;\n integrity: string;\n permissions: Permissions;\n auditScore: number;\n auditStatus: string;\n downloadUrl: string;\n publishedAt: string;\n}\n\nfunction textResult(text: string, isError?: boolean) {\n return {\n content: [{ type: 'text' as const, text }],\n ...(isError ? { isError: true } : {})\n };\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n\nexport function registerInstallSkillTool(server: McpServer): void {\n server.tool(\n 'install-skill',\n `Install a skill from the Tank registry. Resolves version, downloads tarball, verifies SHA-512 integrity, extracts files, and updates ${MANIFEST_FILENAME} + ${LOCKFILE_FILENAME}.`,\n {\n name: z.string().describe('Skill name in @org/name format'),\n version: z.string().optional().describe('Specific version or semver range (default: latest)'),\n directory: z.string().optional().describe('Project directory (defaults to current working directory)')\n },\n async ({ name, version: versionRange, directory }) => {\n // 1. Validate scoped name\n if (!SCOPED_NAME_PATTERN.test(name)) {\n return textResult(\n `Validation error: Skill name \"${name}\" must use the @org/name format (e.g. @acme/my-skill).`,\n true\n );\n }\n\n const client = new TankApiClient();\n\n // 2. Check authentication\n if (!client.isAuthenticated) {\n return textResult('Not authenticated. Use the \"login\" tool first to authenticate with Tank.', true);\n }\n\n const dir = directory ? path.resolve(directory) : process.cwd();\n const range = versionRange ?? '*';\n\n // 3. Read or create manifest (tank.json, with skills.json fallback)\n let skillsJsonPath = path.join(dir, MANIFEST_FILENAME);\n if (!fs.existsSync(skillsJsonPath) && fs.existsSync(path.join(dir, LEGACY_MANIFEST_FILENAME))) {\n skillsJsonPath = path.join(dir, LEGACY_MANIFEST_FILENAME);\n }\n let skillsJson: Record<string, unknown> = { skills: {} };\n if (fs.existsSync(skillsJsonPath)) {\n try {\n const raw = fs.readFileSync(skillsJsonPath, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return textResult(`Failed to read or parse ${path.basename(skillsJsonPath)}.`, true);\n }\n } else {\n skillsJsonPath = path.join(dir, MANIFEST_FILENAME);\n skillsJson = { skills: {} };\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n }\n\n // 4. Read existing lockfile (tank.lock, with skills.lock fallback)\n let lockPath = path.join(dir, LOCKFILE_FILENAME);\n if (!fs.existsSync(lockPath) && fs.existsSync(path.join(dir, LEGACY_LOCKFILE_FILENAME))) {\n lockPath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n }\n let lock: SkillsLock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n if (fs.existsSync(lockPath)) {\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n }\n\n // 5. Fetch available versions\n const encodedName = encodeURIComponent(name);\n const versionsResult = await client.fetch<{ name: string; versions: VersionInfo[] }>(\n `/api/v1/skills/${encodedName}/versions`\n );\n\n if (!versionsResult.ok) {\n if (versionsResult.status === 401 || versionsResult.status === 403) {\n return textResult('Authentication failed. Use the \"login\" tool to authenticate with Tank.', true);\n }\n if (versionsResult.status === 404) {\n return textResult(`Skill not found: \"${name}\" does not exist in the Tank registry.`, true);\n }\n if (versionsResult.status === 0) {\n return textResult(\n `Cannot reach the Tank registry. Check your network connection and try again.\\nError: ${versionsResult.error}`,\n true\n );\n }\n return textResult(`Failed to fetch versions for ${name}: ${versionsResult.error}`, true);\n }\n\n const availableVersions = versionsResult.data.versions.map((v) => v.version);\n\n // 6. Resolve best version\n const resolved = resolve(range, availableVersions);\n if (!resolved) {\n return textResult(\n `No version of ${name} satisfies range \"${range}\". Available versions: ${availableVersions.join(', ')}`,\n true\n );\n }\n\n // 7. Check if already installed\n const lockKey = `${name}@${resolved}`;\n if (lock.skills[lockKey]) {\n return textResult(`${name}@${resolved} is already installed. No changes needed.`);\n }\n\n // 8. Fetch version metadata\n const metaResult = await client.fetch<VersionMetadata>(`/api/v1/skills/${encodedName}/${resolved}`);\n\n if (!metaResult.ok) {\n if (metaResult.status === 404) {\n return textResult(`Version ${resolved} of ${name} not found in the registry.`, true);\n }\n return textResult(`Failed to fetch metadata for ${name}@${resolved}: ${metaResult.error}`, true);\n }\n\n const metadata = metaResult.data;\n\n // 9. Download tarball\n let tarballBuffer: Buffer;\n try {\n const downloadRes = await fetch(metadata.downloadUrl);\n if (!downloadRes.ok) {\n return textResult(\n `Failed to download tarball for ${name}@${resolved}: ${downloadRes.status} ${downloadRes.statusText}`,\n true\n );\n }\n tarballBuffer = Buffer.from(await downloadRes.arrayBuffer());\n } catch (err) {\n return textResult(\n `Network error downloading tarball for ${name}@${resolved}: ${err instanceof Error ? err.message : String(err)}`,\n true\n );\n }\n\n // 10. Verify SHA-512 integrity\n const hash = crypto.createHash('sha512').update(tarballBuffer).digest('base64');\n const computedIntegrity = `sha512-${hash}`;\n\n if (computedIntegrity !== metadata.integrity) {\n return textResult(\n `Integrity verification failed for ${name}@${resolved}.\\n` +\n `Expected: ${metadata.integrity}\\n` +\n `Got: ${computedIntegrity}\\n\\n` +\n 'The tarball may have been tampered with. No files were extracted.',\n true\n );\n }\n\n // 11. Extract tarball safely\n const extractDir = getSkillDir(dir, name);\n fs.mkdirSync(extractDir, { recursive: true });\n\n try {\n await extractSafely(tarballBuffer, extractDir);\n } catch (err) {\n // Clean up on extraction failure\n fs.rmSync(extractDir, { recursive: true, force: true });\n return textResult(\n `Failed to extract tarball for ${name}@${resolved}: ${err instanceof Error ? err.message : String(err)}`,\n true\n );\n }\n\n // 12. Update skills.json\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n skills[name] = range === '*' ? `^${resolved}` : range;\n skillsJson.skills = skills;\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n\n // 13. Update skills.lock\n lock.skills[lockKey] = {\n resolved: metadata.downloadUrl,\n integrity: computedIntegrity,\n permissions: metadata.permissions ?? {},\n audit_score: metadata.auditScore ?? null\n };\n\n // Sort keys alphabetically for determinism\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n\n fs.mkdirSync(path.dirname(lockPath), { recursive: true });\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n\n // Build response\n const score =\n metadata.auditScore !== null && metadata.auditScore !== undefined\n ? `${metadata.auditScore.toFixed(1)}/10`\n : 'pending';\n\n const lines: string[] = [\n `## Installed ${name}@${resolved}`,\n '',\n `**Integrity:** SHA-512 verified`,\n `**Audit Score:** ${score}`,\n `**Extracted to:** ${extractDir}`,\n '',\n '### Updated files',\n `- ${path.basename(skillsJsonPath)}: added \"${name}\": \"${skills[name]}\"`,\n `- ${path.basename(lockPath)}: added ${lockKey}`\n ];\n\n return textResult(lines.join('\\n'));\n }\n );\n}\n\n/**\n * Extract a tarball safely with security checks.\n * Rejects: absolute paths, path traversal (..), symlinks/hardlinks.\n */\nasync function extractSafely(tarball: Buffer, destDir: string): Promise<void> {\n const tmpTarball = path.join(destDir, '.tmp-tarball.tgz');\n fs.writeFileSync(tmpTarball, tarball);\n\n try {\n await extract({\n file: tmpTarball,\n cwd: destDir,\n filter: (entryPath: string) => {\n if (path.isAbsolute(entryPath)) {\n throw new Error(`Absolute path in tarball: ${entryPath}`);\n }\n if (entryPath.split('/').includes('..') || entryPath.split(path.sep).includes('..')) {\n throw new Error(`Path traversal in tarball: ${entryPath}`);\n }\n return true;\n },\n onReadEntry: (entry) => {\n if (entry.type === 'SymbolicLink' || entry.type === 'Link') {\n throw new Error(`Symlink/hardlink in tarball: ${entry.path}`);\n }\n }\n });\n } finally {\n if (fs.existsSync(tmpTarball)) {\n fs.unlinkSync(tmpTarball);\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\n\nexport function registerLinkSkillTool(server: McpServer): void {\n server.tool(\n 'link-skill',\n 'Link an installed skill into an agent workspace. Creates a symlink from the workspace .skills directory to the installed skill.',\n {\n name: z.string().describe('Skill name in @org/name format'),\n workspace: z.string().describe('Agent workspace directory path'),\n directory: z\n .string()\n .optional()\n .describe('Project directory where skills are installed (defaults to current working directory)')\n },\n async ({ name, workspace, directory }) => {\n if (!SCOPED_NAME_PATTERN.test(name)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Validation error: Skill name \"${name}\" must use the @org/name format (e.g. @acme/my-skill).`\n }\n ],\n isError: true\n };\n }\n\n const projectDir = directory ? path.resolve(directory) : process.cwd();\n const workspaceDir = path.resolve(workspace);\n\n if (!fs.existsSync(workspaceDir)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: Workspace directory does not exist: ${workspaceDir}`\n }\n ],\n isError: true\n };\n }\n\n const skillDir = getSkillDir(projectDir, name);\n if (!fs.existsSync(skillDir)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" is not installed. Install it first with \"install-skill\" before linking.`\n }\n ],\n isError: true\n };\n }\n\n const [scope, skillName] = name.split('/');\n const skillsLinkDir = path.join(workspaceDir, '.skills', scope);\n const symlinkPath = path.join(skillsLinkDir, skillName);\n\n try {\n const stats = fs.lstatSync(symlinkPath);\n if (stats.isSymbolicLink()) {\n const currentTarget = fs.readlinkSync(symlinkPath);\n const resolvedTarget = path.isAbsolute(currentTarget)\n ? currentTarget\n : path.resolve(path.dirname(symlinkPath), currentTarget);\n\n if (path.resolve(resolvedTarget) === path.resolve(skillDir)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" is already linked in ${workspaceDir}.`\n }\n ]\n };\n }\n\n fs.unlinkSync(symlinkPath);\n }\n } catch {}\n\n fs.mkdirSync(skillsLinkDir, { recursive: true });\n fs.symlinkSync(skillDir, symlinkPath, 'dir');\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Successfully linked \"${name}\" into ${workspaceDir}.\\nSymlink: ${symlinkPath} → ${skillDir}`\n }\n ]\n };\n }\n );\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\nimport { getConfig, setConfig } from '../lib/config.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\nexport function registerLoginTool(server: McpServer): void {\n server.tool(\n 'login',\n 'Authenticate with Tank using GitHub OAuth device flow. Opens browser for authorization.',\n {\n timeout: z.number().optional().describe('Timeout in milliseconds (default: 300000 = 5 minutes)')\n },\n async ({ timeout = DEFAULT_TIMEOUT_MS }) => {\n const client = new TankApiClient();\n const config = getConfig();\n\n // Check if already logged in with valid token\n if (config.token) {\n const authCheck = await client.verifyAuth();\n if (authCheck.valid) {\n const displayName = authCheck.user?.name ?? authCheck.user?.email ?? 'unknown user';\n return {\n content: [\n {\n type: 'text' as const,\n text: `Already logged in as ${displayName}.\\n\\nTo log out, delete ~/.tank/config.json or use the CLI: tank logout`\n }\n ]\n };\n }\n }\n\n // Start device flow\n const state = crypto.randomUUID();\n const startRes = await fetch(`${config.registry}/api/v1/cli-auth/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ state })\n });\n\n if (!startRes.ok) {\n const body = await startRes.json().catch(() => ({}));\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to start login flow: ${(body as { error?: string }).error ?? startRes.statusText}`\n }\n ]\n };\n }\n\n const { sessionCode } = (await startRes.json()) as {\n sessionCode: string;\n };\n\n // Return auth URL and poll for completion\n const deadline = Date.now() + timeout;\n const _authorized = false;\n let lastStatus = '';\n\n while (Date.now() < deadline) {\n try {\n const exchangeRes = await fetch(`${config.registry}/api/v1/cli-auth/exchange`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionCode, state })\n });\n\n if (exchangeRes.ok) {\n const { token, user } = (await exchangeRes.json()) as {\n token: string;\n user: { name: string | null; email: string | null };\n };\n\n // Save token to config\n setConfig({ token, user: user as { name: string; email: string } });\n\n const displayName = user.name ?? user.email ?? 'unknown user';\n return {\n content: [\n {\n type: 'text' as const,\n text: `Successfully logged in as ${displayName}!\\n\\nYou can now use all Tank MCP tools: scan-skill, publish-skill, etc.`\n }\n ]\n };\n }\n\n // 400 means not yet authorized - keep polling\n if (exchangeRes.status !== 400) {\n const body = await exchangeRes.json().catch(() => ({}));\n return {\n content: [\n {\n type: 'text' as const,\n text: `Login failed: ${(body as { error?: string }).error ?? exchangeRes.statusText}`\n }\n ]\n };\n }\n\n // Check status and provide updates\n const newStatus = 'Waiting for authorization...';\n if (newStatus !== lastStatus) {\n lastStatus = newStatus;\n }\n } catch {\n // Network errors during polling are transient\n }\n\n await new Promise((resolve) => setTimeout(resolve, DEFAULT_POLL_INTERVAL_MS));\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Login timed out. The authorization link may have expired.\\n\\nTry again: tank login`\n }\n ]\n };\n }\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { getConfig, setConfig } from '../lib/config.js';\n\nexport function registerLogoutTool(server: McpServer): void {\n server.tool('logout', 'Log out of Tank by clearing local credentials.', {}, async () => {\n const config = getConfig();\n\n if (!config.token) {\n return {\n content: [{ type: 'text' as const, text: 'Not logged in. No credentials to clear.' }]\n };\n }\n\n setConfig({ token: undefined, user: undefined });\n\n // Also clear env-based token so subsequent tool calls in this\n // process don't re-read it via getConfig().\n delete process.env.TANK_TOKEN;\n\n return {\n content: [{ type: 'text' as const, text: 'Successfully logged out.' }]\n };\n });\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable } from 'node:stream';\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME, skillsJsonSchema } from '@internal/shared';\nimport ignore from 'ignore';\nimport { create } from 'tar';\n\n// Limits\nconst MAX_PACKAGE_SIZE = 50 * 1024 * 1024; // 50MB\nconst MAX_FILE_COUNT = 1000;\n\n// Default ignore patterns\nconst DEFAULT_IGNORES = ['node_modules', '.git', '.env*', '*.log', '.tank', '.DS_Store'];\n\n// Always ignored regardless of ignore file contents\nconst ALWAYS_IGNORED = ['node_modules', '.git'];\n\n// Ignore file names (not packed into tarball)\nconst IGNORE_FILES = ['.tankignore', '.gitignore'];\n\nexport interface PackResult {\n tarball: Buffer;\n integrity: string; // \"sha512-{base64}\"\n fileCount: number;\n totalSize: number;\n readme: string;\n files: string[];\n manifest: Record<string, unknown>;\n}\n\n/**\n * Pack a skill directory into a .tgz tarball with integrity hashing.\n */\nexport async function pack(directory: string): Promise<PackResult> {\n const absDir = path.resolve(directory);\n\n // 1. Verify directory exists\n if (!fs.existsSync(absDir)) {\n throw new Error(`Directory does not exist: ${absDir}`);\n }\n\n const stat = fs.statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`);\n }\n\n // 2. Verify manifest (tank.json or skills.json) exists and is valid\n let manifestPath = path.join(absDir, MANIFEST_FILENAME);\n let manifestFilename = MANIFEST_FILENAME;\n if (!fs.existsSync(manifestPath)) {\n manifestPath = path.join(absDir, LEGACY_MANIFEST_FILENAME);\n manifestFilename = LEGACY_MANIFEST_FILENAME;\n }\n if (!fs.existsSync(manifestPath)) {\n throw new Error(`Missing required file: ${MANIFEST_FILENAME}`);\n }\n\n let skillsJsonContent: string;\n try {\n skillsJsonContent = fs.readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(`Failed to read ${manifestFilename}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(skillsJsonContent);\n } catch {\n throw new Error(`Invalid ${manifestFilename}: not valid JSON`);\n }\n\n const validation = skillsJsonSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid ${manifestFilename}:\\n${issues}`);\n }\n\n // 3. Verify SKILL.md exists and read its content\n const skillMdPath = path.join(absDir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) {\n throw new Error('Missing required file: SKILL.md');\n }\n\n let readmeContent: string;\n try {\n readmeContent = fs.readFileSync(skillMdPath, 'utf-8');\n } catch {\n throw new Error('Failed to read SKILL.md');\n }\n\n // 4. Build ignore filter\n const ig = buildIgnoreFilter(absDir);\n\n // 5. Collect files with validation\n const files = collectFiles(absDir, absDir, ig);\n\n // 6. Enforce file count limit\n if (files.length > MAX_FILE_COUNT) {\n throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);\n }\n\n // 7. Calculate total size of source files\n let totalSize = 0;\n for (const file of files) {\n const filePath = path.join(absDir, file);\n const fileStat = fs.statSync(filePath);\n totalSize += fileStat.size;\n }\n\n // 8. Create tarball\n const tarball = await createTarball(absDir, files);\n\n // 9. Enforce tarball size limit\n if (tarball.length > MAX_PACKAGE_SIZE) {\n throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);\n }\n\n // 10. Compute integrity hash\n const hash = crypto.createHash('sha512').update(tarball).digest('base64');\n const integrity = `sha512-${hash}`;\n\n return {\n tarball,\n integrity,\n fileCount: files.length,\n totalSize,\n readme: readmeContent,\n files,\n manifest: validation.data as Record<string, unknown>\n };\n}\n\n/**\n * Pack a directory into a .tgz tarball for security scanning.\n *\n * Unlike pack(), this function does NOT require skills.json or SKILL.md.\n * It applies the same security checks (no symlinks, no path traversal, etc.)\n * and returns the same PackResult interface with a synthesised manifest.\n *\n * Validates:\n * - Directory exists\n * - No symlinks or hardlinks\n * - No path traversal (.. components)\n * - No absolute paths\n * - File count <= 1000\n * - Tarball size <= 50MB\n *\n * Does NOT validate:\n * - skills.json existence or validity\n * - SKILL.md existence (but reads it if present)\n */\nexport async function packForScan(directory: string): Promise<PackResult> {\n const absDir = path.resolve(directory);\n\n // 1. Verify directory exists\n if (!fs.existsSync(absDir)) {\n throw new Error(`Directory does not exist: ${absDir}`);\n }\n\n const stat = fs.statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`);\n }\n\n // 2. Try to read SKILL.md if it exists (optional for scan)\n let readmeContent = '';\n const skillMdPath = path.join(absDir, 'SKILL.md');\n if (fs.existsSync(skillMdPath)) {\n try {\n readmeContent = fs.readFileSync(skillMdPath, 'utf-8');\n } catch {\n readmeContent = '';\n }\n }\n\n // 3. Build ignore filter\n const ig = buildIgnoreFilter(absDir);\n\n // 4. Collect files with validation\n const files = collectFiles(absDir, absDir, ig);\n\n // 5. Enforce file count limit\n if (files.length > MAX_FILE_COUNT) {\n throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);\n }\n\n // 6. Check for empty directory (no files to scan)\n if (files.length === 0) {\n throw new Error('No files to scan: directory is empty or all files are ignored');\n }\n\n // 7. Calculate total size of source files\n let totalSize = 0;\n for (const file of files) {\n const filePath = path.join(absDir, file);\n const fileStat = fs.statSync(filePath);\n totalSize += fileStat.size;\n }\n\n // 8. Create tarball\n const tarball = await createTarball(absDir, files);\n\n // 9. Enforce tarball size limit\n if (tarball.length > MAX_PACKAGE_SIZE) {\n throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);\n }\n\n // 10. Compute integrity hash\n const hash = crypto.createHash('sha512').update(tarball).digest('base64');\n const integrity = `sha512-${hash}`;\n\n // 11. Synthesise a minimal manifest\n const dirName = path.basename(absDir);\n const manifest: Record<string, unknown> = {\n name: dirName,\n version: '0.0.0',\n description: 'Local scan'\n };\n\n return {\n tarball,\n integrity,\n fileCount: files.length,\n totalSize,\n readme: readmeContent,\n files,\n manifest\n };\n}\n\n/**\n * Build an ignore filter from .tankignore, .gitignore, or defaults.\n */\nfunction buildIgnoreFilter(dir: string): ReturnType<typeof ignore> {\n const ig = ignore();\n\n ig.add(ALWAYS_IGNORED);\n\n const tankIgnorePath = path.join(dir, '.tankignore');\n const gitIgnorePath = path.join(dir, '.gitignore');\n\n if (fs.existsSync(tankIgnorePath)) {\n const content = fs.readFileSync(tankIgnorePath, 'utf-8');\n ig.add(content);\n ig.add(IGNORE_FILES);\n } else if (fs.existsSync(gitIgnorePath)) {\n const content = fs.readFileSync(gitIgnorePath, 'utf-8');\n ig.add(content);\n ig.add(IGNORE_FILES);\n } else {\n ig.add(DEFAULT_IGNORES);\n }\n\n return ig;\n}\n\n/**\n * Recursively collect files from a directory.\n */\nfunction collectFiles(baseDir: string, currentDir: string, ig: ReturnType<typeof ignore>): string[] {\n const files: string[] = [];\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n const relativePath = path.relative(baseDir, fullPath);\n\n // Security: check for path traversal\n if (relativePath.split(path.sep).includes('..')) {\n throw new Error(`Path traversal detected: \"${relativePath}\" contains \"..\" component`);\n }\n\n // Security: check for absolute paths\n if (path.isAbsolute(relativePath)) {\n throw new Error(`Absolute path detected: \"${relativePath}\"`);\n }\n\n // Security: check for symlinks\n const lstatResult = fs.lstatSync(fullPath);\n if (lstatResult.isSymbolicLink()) {\n throw new Error(`Symlink detected: \"${relativePath}\" — symlinks are not allowed`);\n }\n\n const pathForIgnore = lstatResult.isDirectory() ? `${relativePath}/` : relativePath;\n\n if (ig.ignores(pathForIgnore)) {\n continue;\n }\n\n if (lstatResult.isDirectory()) {\n const subFiles = collectFiles(baseDir, fullPath, ig);\n files.push(...subFiles);\n } else if (lstatResult.isFile()) {\n files.push(relativePath);\n }\n }\n\n return files;\n}\n\n/**\n * Create a gzipped tarball from the given files.\n */\nasync function createTarball(cwd: string, files: string[]): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n const stream = create(\n {\n gzip: true,\n cwd,\n portable: true\n },\n files\n ) as unknown as Readable;\n\n stream.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n stream.on('end', () => {\n resolve(Buffer.concat(chunks));\n });\n\n stream.on('error', (err: Error) => {\n reject(err);\n });\n });\n}\n","import path from 'node:path';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\nimport { type PackResult, pack } from '../lib/packer.js';\n\ninterface PublishStartResponse {\n uploadUrl: string;\n skillId: string;\n versionId: string;\n}\n\ninterface PublishConfirmResponse {\n success: boolean;\n name: string;\n version: string;\n auditScore: number | null;\n scanVerdict: string | null;\n}\n\nexport function registerPublishSkillTool(server: McpServer): void {\n server.tool(\n 'publish-skill',\n 'Publish a skill to the Tank registry. Requires authentication.',\n {\n directory: z.string().optional().describe('Directory to publish (default: current directory)'),\n visibility: z.enum(['public', 'private']).optional().default('public').describe('Package visibility'),\n dryRun: z.boolean().optional().default(false).describe('Validate without publishing')\n },\n async ({ directory = '.', visibility = 'public', dryRun = false }) => {\n const absDir = path.resolve(directory);\n const client = new TankApiClient();\n\n // Check auth (skip for dry run)\n if (!dryRun && !client.isAuthenticated) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'You need to log in first. Use the login tool to authenticate with Tank.\\n\\nExample: \"Log in to Tank\"'\n }\n ]\n };\n }\n\n if (!dryRun) {\n const authCheck = await client.verifyAuth();\n if (!authCheck.valid) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Your session has expired. Use the login tool to authenticate again.'\n }\n ]\n };\n }\n }\n\n // Pack the skill\n let packResult: PackResult;\n try {\n packResult = await pack(absDir);\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to pack skill: ${err instanceof Error ? err.message : String(err)}`\n }\n ]\n };\n }\n\n const manifest = packResult.manifest as { name?: string; version?: string; description?: string };\n const skillName = manifest.name ?? 'unknown';\n const skillVersion = manifest.version ?? '0.0.0';\n\n // Dry run: just validate and return summary\n if (dryRun) {\n const lines: string[] = [\n `## Dry Run for ${skillName}@${skillVersion}`,\n '',\n '**Validation:** ✅ PASSED',\n '',\n '### Package Summary',\n `- **Name:** ${skillName}`,\n `- **Version:** ${skillVersion}`,\n `- **Visibility:** ${visibility}`,\n `- **Files:** ${packResult.fileCount}`,\n `- **Size:** ${(packResult.totalSize / 1024).toFixed(1)}KB compressed`,\n `- **Integrity:** ${packResult.integrity.slice(0, 20)}...`,\n '',\n '### Manifest',\n `- **Description:** ${manifest.description ?? 'No description'}`,\n `- **Permissions:** ${JSON.stringify((manifest as Record<string, unknown>).permissions ?? {})}`,\n '',\n '### Files',\n ...packResult.files.slice(0, 10).map((f) => ` - ${f}`),\n packResult.files.length > 10 ? ` ... and ${packResult.files.length - 10} more` : '',\n '',\n 'Ready to publish. Say \"publish my skill\" when you\\'re ready.'\n ];\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }]\n };\n }\n\n // Step 1: Start publish flow\n const startResult = await client.fetch<PublishStartResponse>('/api/v1/skills', {\n method: 'POST',\n body: JSON.stringify({\n manifest: { ...manifest, visibility },\n readme: packResult.readme,\n files: packResult.files\n })\n });\n\n if (!startResult.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to start publish: ${startResult.error}`\n }\n ]\n };\n }\n\n const { uploadUrl, versionId } = startResult.data;\n\n // Step 2: Upload tarball\n const uploadRes = await fetch(uploadUrl, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/gzip'\n },\n body: new Uint8Array(packResult.tarball)\n });\n\n if (!uploadRes.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to upload tarball: ${uploadRes.statusText}`\n }\n ]\n };\n }\n\n // Step 3: Confirm upload\n const confirmResult = await client.fetch<PublishConfirmResponse>('/api/v1/skills/confirm', {\n method: 'POST',\n body: JSON.stringify({\n versionId,\n integrity: packResult.integrity,\n fileCount: packResult.fileCount,\n tarballSize: packResult.tarball.length,\n readme: packResult.readme\n })\n });\n\n if (!confirmResult.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to confirm publish: ${confirmResult.error}`\n }\n ]\n };\n }\n\n const confirm = confirmResult.data;\n const score = confirm.auditScore !== null ? `${confirm.auditScore.toFixed(1)}/10` : 'pending';\n\n const lines: string[] = [\n `## Published ${confirm.name}@${confirm.version}`,\n '',\n `**Status:** ✅ Successfully published`,\n `**Visibility:** ${visibility}`,\n `**Audit Score:** ${score}`,\n `**Scan Verdict:** ${confirm.scanVerdict ?? 'pending'}`,\n '',\n '### Package Details',\n `- **Files:** ${packResult.fileCount}`,\n `- **Size:** ${(packResult.totalSize / 1024).toFixed(1)}KB`,\n '',\n `View your skill: https://tankpkg.dev/skills/${confirm.name}`\n ];\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }]\n };\n }\n );\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n LOCKFILE_VERSION,\n MANIFEST_FILENAME,\n type SkillsLock\n} from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\n\nexport function registerRemoveSkillTool(server: McpServer): void {\n server.tool(\n 'remove-skill',\n `Remove an installed skill from the project. Removes from ${MANIFEST_FILENAME}, ${LOCKFILE_FILENAME}, and deletes skill files.`,\n {\n name: z.string().describe('Skill name in @org/name format'),\n directory: z.string().optional().describe('Project directory (defaults to current working directory)')\n },\n async ({ name, directory }) => {\n if (!SCOPED_NAME_PATTERN.test(name)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Validation error: Skill name \"${name}\" must use the @org/name format (e.g. @acme/my-skill).`\n }\n ],\n isError: true\n };\n }\n\n const dir = directory ? path.resolve(directory) : process.cwd();\n const results: string[] = [];\n let skillFoundAnywhere = false;\n\n // Find manifest (tank.json or skills.json)\n let skillsJsonPath = path.join(dir, MANIFEST_FILENAME);\n if (!fs.existsSync(skillsJsonPath)) {\n skillsJsonPath = path.join(dir, LEGACY_MANIFEST_FILENAME);\n }\n if (fs.existsSync(skillsJsonPath)) {\n try {\n const raw = fs.readFileSync(skillsJsonPath, 'utf-8');\n const skillsJson = JSON.parse(raw) as Record<string, unknown>;\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n\n if (name in skills) {\n skillFoundAnywhere = true;\n delete skills[name];\n skillsJson.skills = skills;\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n results.push(`Removed \"${name}\" from ${path.basename(skillsJsonPath)}`);\n }\n } catch {\n results.push(`Warning: Failed to read or parse ${path.basename(skillsJsonPath)}`);\n }\n }\n\n // Find lockfile (tank.lock or skills.lock)\n let lockPath = path.join(dir, LOCKFILE_FILENAME);\n if (!fs.existsSync(lockPath)) {\n lockPath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n }\n if (fs.existsSync(lockPath)) {\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n const lock = JSON.parse(raw) as SkillsLock;\n let removedFromLock = false;\n\n for (const key of Object.keys(lock.skills)) {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) continue;\n const keyName = key.slice(0, lastAt);\n if (keyName === name) {\n delete lock.skills[key];\n removedFromLock = true;\n skillFoundAnywhere = true;\n }\n }\n\n if (removedFromLock) {\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n results.push(`Removed \"${name}\" from ${path.basename(lockPath)}`);\n }\n } catch {\n results.push(`Warning: Failed to read or parse ${path.basename(lockPath)}`);\n }\n }\n\n const skillDir = getSkillDir(dir, name);\n if (fs.existsSync(skillDir)) {\n skillFoundAnywhere = true;\n fs.rmSync(skillDir, { recursive: true, force: true });\n results.push(`Deleted skill files from ${skillDir}`);\n } else {\n results.push(`Skill files were already absent from ${skillDir}`);\n }\n\n const symlinkName = name.replace(/\\//g, '__');\n const agentSkillDir = path.join(dir, '.tank', 'agent-skills', symlinkName);\n if (fs.existsSync(agentSkillDir)) {\n fs.rmSync(agentSkillDir, { recursive: true, force: true });\n results.push('Removed symlink from agent workspace');\n }\n\n if (!skillFoundAnywhere) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" is not installed. It was not found in ${MANIFEST_FILENAME}, ${LOCKFILE_FILENAME}, or .tank/skills/.`\n }\n ],\n isError: true\n };\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `Successfully removed ${name}.\\n${results.join('\\n')}`\n }\n ]\n };\n }\n );\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME } from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\nimport { getConfig } from '../lib/config.js';\nimport { pack, packForScan } from '../lib/packer.js';\n\ninterface ScanFinding {\n stage: string;\n severity: 'critical' | 'high' | 'medium' | 'low';\n type: string;\n description: string;\n location: string | null;\n confidence: number | null;\n tool: string | null;\n evidence: string | null;\n}\n\ninterface LLMAnalysis {\n enabled: boolean;\n mode: 'byollm' | 'builtin' | 'disabled';\n provider_used?: string;\n findings_reviewed?: number;\n findings_dismissed?: number;\n findings_confirmed?: number;\n findings_uncertain?: number;\n latency_ms?: number;\n error?: string;\n}\n\ninterface ScanResponse {\n scan_id: string | null;\n verdict: 'pass' | 'pass_with_notes' | 'flagged' | 'fail';\n audit_score: number;\n findings: ScanFinding[];\n stage_results: Array<{\n stage: string;\n status: string;\n findings: ScanFinding[];\n duration_ms: number;\n }>;\n llm_analysis?: LLMAnalysis;\n duration_ms: number;\n}\n\nexport function registerScanSkillTool(server: McpServer): void {\n server.tool(\n 'scan-skill',\n 'Scan a skill directory for security issues. Requires authentication.',\n {\n directory: z.string().optional().describe('Directory to scan (default: current directory)')\n },\n async ({ directory = '.' }) => {\n const absDir = path.resolve(directory);\n const client = new TankApiClient();\n\n // Check auth, guide user to login if not authenticated\n if (!client.isAuthenticated) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'You need to log in first. Use the login tool to authenticate with Tank.\\n\\nExample: \"Log in to Tank\"'\n }\n ]\n };\n }\n\n // Verify auth is still valid\n const authCheck = await client.verifyAuth();\n if (!authCheck.valid) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Your session has expired. Use the login tool to authenticate again.\\n\\nExample: \"Log in to Tank\"'\n }\n ]\n };\n }\n\n let packResult: Awaited<ReturnType<typeof pack>>;\n let usedSynthesisedManifest = false;\n\n const hasManifest =\n fs.existsSync(path.join(absDir, MANIFEST_FILENAME)) ||\n fs.existsSync(path.join(absDir, LEGACY_MANIFEST_FILENAME));\n\n if (hasManifest) {\n try {\n packResult = await pack(absDir);\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to pack skill: ${err instanceof Error ? err.message : String(err)}`\n }\n ]\n };\n }\n } else {\n try {\n packResult = await packForScan(absDir);\n usedSynthesisedManifest = true;\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to pack directory for scan: ${err instanceof Error ? err.message : String(err)}`\n }\n ]\n };\n }\n }\n\n const manifest = packResult.manifest as { name?: string; version?: string };\n const skillName = manifest.name ?? 'unknown';\n const skillVersion = manifest.version ?? '0.0.0';\n\n // Upload tarball and trigger scan\n const formData = new FormData();\n const blob = new Blob([new Uint8Array(packResult.tarball)], { type: 'application/gzip' });\n formData.append('tarball', blob, `${skillName}-${skillVersion}.tgz`);\n formData.append('manifest', JSON.stringify(manifest));\n\n const config = getConfig();\n const scanRes = await fetch(`${config.registry}/api/v1/scan`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${client.token}`\n },\n body: formData\n });\n\n if (!scanRes.ok) {\n const body = await scanRes.json().catch(() => ({}));\n return {\n content: [\n {\n type: 'text' as const,\n text: `Scan failed: ${(body as { error?: string }).error ?? scanRes.statusText}`\n }\n ]\n };\n }\n\n const scanResult = (await scanRes.json()) as ScanResponse;\n\n // Format report\n const verdictEmoji: Record<string, string> = {\n pass: '✅',\n pass_with_notes: '⚠️',\n flagged: '🚩',\n fail: '❌'\n };\n\n const severityEmoji: Record<string, string> = {\n critical: '🔴',\n high: '🟠',\n medium: '🟡',\n low: '🟢'\n };\n\n const lines: string[] = [`## Scan Results for ${skillName}@${skillVersion}`, ''];\n\n if (usedSynthesisedManifest) {\n lines.push(`> **Note:** No \\`${MANIFEST_FILENAME}\\` found. A synthesised manifest was used for scanning.`);\n lines.push('');\n }\n\n const auditScore = scanResult.audit_score ?? 0;\n const durationMs = scanResult.duration_ms ?? 0;\n\n lines.push(\n `**Verdict:** ${verdictEmoji[scanResult.verdict] ?? ''} ${scanResult.verdict.toUpperCase()}`,\n `**Score:** ${auditScore.toFixed(1)}/10`,\n `**Duration:** ${(durationMs / 1000).toFixed(1)}s`,\n `**Files:** ${packResult.fileCount} (${(packResult.totalSize / 1024).toFixed(1)}KB)`,\n ''\n );\n\n if (scanResult.findings.length > 0) {\n lines.push(`### Findings (${scanResult.findings.length})`);\n lines.push('');\n\n // Group by severity\n const bySeverity: Record<string, ScanFinding[]> = {\n critical: [],\n high: [],\n medium: [],\n low: []\n };\n for (const f of scanResult.findings) {\n bySeverity[f.severity].push(f);\n }\n\n for (const severity of ['critical', 'high', 'medium', 'low'] as const) {\n const findings = bySeverity[severity];\n if (findings.length === 0) continue;\n\n lines.push(`#### ${severityEmoji[severity]} ${severity.toUpperCase()} (${findings.length})`);\n for (const f of findings) {\n lines.push(`- **${f.type}**: ${f.description}`);\n if (f.location) lines.push(` - Location: ${f.location}`);\n }\n lines.push('');\n }\n } else {\n lines.push('No findings. Your skill looks secure!');\n lines.push('');\n }\n\n // Stages run\n if (scanResult.stage_results?.length > 0) {\n lines.push('### Scan Stages');\n lines.push('');\n for (const stage of scanResult.stage_results) {\n const status = stage.status === 'passed' ? '✓' : '✗';\n lines.push(`- ${status} ${stage.stage} (${stage.duration_ms}ms)`);\n }\n lines.push('');\n }\n\n // LLM Analysis\n if (scanResult.llm_analysis?.enabled) {\n const llm = scanResult.llm_analysis;\n lines.push('### LLM Analysis');\n lines.push('');\n lines.push(`**Mode:** ${llm.mode}`);\n if (llm.provider_used) {\n lines.push(`**Provider:** ${llm.provider_used}`);\n }\n if (llm.findings_reviewed !== undefined && llm.findings_reviewed > 0) {\n lines.push(`**Findings Reviewed:** ${llm.findings_reviewed}`);\n }\n if (llm.findings_dismissed !== undefined && llm.findings_dismissed > 0) {\n lines.push(`**False Positives Dismissed:** ${llm.findings_dismissed}`);\n }\n if (llm.findings_confirmed !== undefined && llm.findings_confirmed > 0) {\n lines.push(`**Threats Confirmed:** ${llm.findings_confirmed}`);\n }\n if (llm.findings_uncertain !== undefined && llm.findings_uncertain > 0) {\n lines.push(`**Uncertain:** ${llm.findings_uncertain}`);\n }\n if (llm.latency_ms) {\n lines.push(`**Latency:** ${llm.latency_ms}ms`);\n }\n if (llm.error) {\n lines.push(`**Error:** ${llm.error}`);\n }\n lines.push('');\n }\n\n if (scanResult.scan_id) {\n lines.push(`View full report: https://tankpkg.dev/scans/${scanResult.scan_id}`);\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }]\n };\n }\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\n\ninterface SearchResult {\n name: string;\n version: string;\n description: string | null;\n auditScore: number | null;\n downloads: number;\n publisher: string;\n}\n\ninterface SearchResponse {\n results: SearchResult[];\n total: number;\n}\n\nexport function registerSearchSkillsTool(server: McpServer): void {\n const client = new TankApiClient();\n\n server.tool(\n 'search-skills',\n 'Search the Tank registry for AI agent skills',\n {\n query: z.string().min(1).describe('Search query (skill name or keywords)'),\n limit: z.number().min(1).max(50).optional().default(10).describe('Maximum results to return')\n },\n async ({ query, limit }) => {\n const result = await client.fetch<SearchResponse>(`/api/v1/search?q=${encodeURIComponent(query)}&limit=${limit}`);\n\n if (!result.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Search failed: ${result.error}`\n }\n ]\n };\n }\n\n const { results, total } = result.data;\n\n if (results.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No skills found matching \"${query}\". Try different keywords or browse the registry at https://tankpkg.dev`\n }\n ]\n };\n }\n\n // Format as markdown table\n const header = '| Skill | Score | Downloads | Description |\\n|-------|-------|-----------|-------------|';\n const rows = results.map((skill) => {\n const score = skill.auditScore !== null ? skill.auditScore.toFixed(1) : '-';\n const downloads =\n skill.downloads > 1000 ? `${(skill.downloads / 1000).toFixed(1)}k` : skill.downloads.toString();\n const desc = skill.description?.slice(0, 50) ?? 'No description';\n return `| ${skill.name} | ${score} | ${downloads} | ${desc} |`;\n });\n\n const text = [\n `Found ${total} skill${total !== 1 ? 's' : ''} matching \"${query}\":`,\n '',\n header,\n ...rows,\n '',\n `View full results: https://tankpkg.dev/search?q=${encodeURIComponent(query)}`\n ].join('\\n');\n\n return {\n content: [{ type: 'text' as const, text }]\n };\n }\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\n\ninterface SkillVersion {\n version: string;\n integrity: string;\n auditScore: number | null;\n auditStatus: string;\n publishedAt: string;\n tarballSize: number;\n fileCount: number;\n}\n\ninterface SkillInfoResponse {\n name: string;\n description: string | null;\n publisher: string;\n latestVersion: string;\n auditScore: number | null;\n auditStatus: string;\n downloads: number;\n permissions: Record<string, unknown> | null;\n versions: SkillVersion[];\n readme: string | null;\n}\n\nexport function registerSkillInfoTool(server: McpServer): void {\n const client = new TankApiClient();\n\n server.tool(\n 'skill-info',\n 'Get detailed information about a specific skill from the Tank registry',\n {\n name: z.string().describe('Skill name (e.g., @org/skill-name or skill-name)')\n },\n async ({ name }) => {\n const result = await client.fetch<SkillInfoResponse>(`/api/v1/skills/${encodeURIComponent(name)}`);\n\n if (!result.ok) {\n if (result.status === 404) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" not found. Search for skills: https://tankpkg.dev/search`\n }\n ]\n };\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to get skill info: ${result.error}`\n }\n ]\n };\n }\n\n const skill = result.data;\n const score = skill.auditScore !== null ? `${skill.auditScore.toFixed(1)}/10` : 'Not scored';\n const size = skill.versions[0] ? `${(skill.versions[0].tarballSize / 1024).toFixed(1)}KB` : 'Unknown';\n\n // Format permissions as readable text\n let permsText = 'None declared';\n if (skill.permissions) {\n const perms: string[] = [];\n const p = skill.permissions as {\n network?: { outbound?: string[] };\n filesystem?: { read?: string[]; write?: string[] };\n subprocess?: boolean;\n };\n if (p.network?.outbound?.length) {\n perms.push(`network: ${p.network.outbound.join(', ')}`);\n }\n if (p.filesystem?.read?.length || p.filesystem?.write?.length) {\n const fsPerms: string[] = [];\n if (p.filesystem.read?.length) fsPerms.push(`read: ${p.filesystem.read.length} paths`);\n if (p.filesystem.write?.length) fsPerms.push(`write: ${p.filesystem.write.length} paths`);\n perms.push(`filesystem (${fsPerms.join(', ')})`);\n }\n if (p.subprocess) perms.push('subprocess: allowed');\n if (perms.length > 0) permsText = perms.join('\\n - ');\n }\n\n const versionsList = skill.versions\n .slice(0, 5)\n .map((v) => {\n const vScore = v.auditScore !== null ? v.auditScore.toFixed(1) : '-';\n return `${v.version} (score: ${vScore})`;\n })\n .join('\\n - ');\n\n const text = [\n `# ${skill.name}`,\n '',\n `**Publisher:** ${skill.publisher}`,\n `**Latest:** ${skill.latestVersion}`,\n `**Score:** ${score}`,\n `**Size:** ${size}`,\n `**Downloads:** ${skill.downloads}`,\n '',\n '**Description:**',\n skill.description ?? 'No description available',\n '',\n '**Permissions:**',\n ` - ${permsText}`,\n '',\n '**Versions:**',\n ` - ${versionsList}`,\n skill.versions.length > 5 ? `\\n ... and ${skill.versions.length - 5} more` : '',\n '',\n `View on Tank: https://tankpkg.dev/skills/${skill.name}`\n ].join('\\n');\n\n return {\n content: [{ type: 'text' as const, text }]\n };\n }\n );\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n MANIFEST_FILENAME,\n type Permissions,\n type SkillsJson,\n type SkillsLock\n} from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nfunction parseSkillName(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt > 0) {\n return key.slice(0, lastAt);\n }\n return key;\n}\n\ninterface PermissionEntry {\n value: string;\n skills: string[];\n}\n\ninterface ResolvedPermissions {\n networkOutbound: PermissionEntry[];\n filesystemRead: PermissionEntry[];\n filesystemWrite: PermissionEntry[];\n subprocess: string[];\n env: PermissionEntry[];\n exec: PermissionEntry[];\n}\n\nfunction collectPermissions(lockfile: SkillsLock): ResolvedPermissions {\n const networkMap = new Map<string, string[]>();\n const fsReadMap = new Map<string, string[]>();\n const fsWriteMap = new Map<string, string[]>();\n const subprocessSkills: string[] = [];\n const envMap = new Map<string, string[]>();\n const execMap = new Map<string, string[]>();\n\n for (const [key, entry] of Object.entries(lockfile.skills)) {\n const skillName = parseSkillName(key);\n const perms = entry.permissions;\n\n if (perms.network?.outbound) {\n for (const domain of perms.network.outbound) {\n const existing = networkMap.get(domain) ?? [];\n existing.push(skillName);\n networkMap.set(domain, existing);\n }\n }\n\n if (perms.filesystem?.read) {\n for (const p of perms.filesystem.read) {\n const existing = fsReadMap.get(p) ?? [];\n existing.push(skillName);\n fsReadMap.set(p, existing);\n }\n }\n\n if (perms.filesystem?.write) {\n for (const p of perms.filesystem.write) {\n const existing = fsWriteMap.get(p) ?? [];\n existing.push(skillName);\n fsWriteMap.set(p, existing);\n }\n }\n\n if (perms.subprocess === true) {\n subprocessSkills.push(skillName);\n }\n\n const rawPerms = perms as Record<string, unknown>;\n if (Array.isArray(rawPerms.env)) {\n for (const envVar of rawPerms.env as string[]) {\n const existing = envMap.get(envVar) ?? [];\n existing.push(skillName);\n envMap.set(envVar, existing);\n }\n }\n\n if (Array.isArray(rawPerms.exec)) {\n for (const cmd of rawPerms.exec as string[]) {\n const existing = execMap.get(cmd) ?? [];\n existing.push(skillName);\n execMap.set(cmd, existing);\n }\n }\n }\n\n const toEntries = (map: Map<string, string[]>): PermissionEntry[] =>\n Array.from(map.entries()).map(([value, skills]) => ({ value, skills }));\n\n return {\n networkOutbound: toEntries(networkMap),\n filesystemRead: toEntries(fsReadMap),\n filesystemWrite: toEntries(fsWriteMap),\n subprocess: subprocessSkills,\n env: toEntries(envMap),\n exec: toEntries(execMap)\n };\n}\n\nfunction formatAttribution(skills: string[]): string {\n return `<- ${skills.join(', ')}`;\n}\n\nfunction formatSection(title: string, entries: PermissionEntry[]): string {\n const lines: string[] = [];\n lines.push(`${title}:`);\n if (entries.length === 0) {\n lines.push(' none');\n } else {\n for (const entry of entries) {\n lines.push(` ${entry.value} ${formatAttribution(entry.skills)}`);\n }\n }\n return lines.join('\\n');\n}\n\n// Wildcard matching: *.example.com matches sub.example.com, * matches all\nfunction isDomainAllowed(domain: string, allowedDomains: string[]): boolean {\n for (const allowed of allowedDomains) {\n if (allowed === '*') return true;\n if (allowed === domain) return true;\n if (allowed.startsWith('*.')) {\n const suffix = allowed.slice(1);\n if (domain.endsWith(suffix) || domain === allowed.slice(2)) {\n return true;\n }\n if (domain === allowed) return true;\n }\n }\n return false;\n}\n\n// Glob matching: ./src/** matches ./src/foo/bar\nfunction isPathAllowed(requestedPath: string, allowedPaths: string[]): boolean {\n for (const allowed of allowedPaths) {\n if (allowed === requestedPath) return true;\n if (allowed.endsWith('/**')) {\n const prefix = allowed.slice(0, -3);\n if (requestedPath.startsWith(prefix)) return true;\n }\n }\n return false;\n}\n\ninterface BudgetViolation {\n category: string;\n value: string;\n skills: string[];\n}\n\nfunction checkBudget(resolved: ResolvedPermissions, budget: Permissions): BudgetViolation[] {\n const violations: BudgetViolation[] = [];\n\n const budgetDomains = budget.network?.outbound ?? [];\n for (const entry of resolved.networkOutbound) {\n if (!isDomainAllowed(entry.value, budgetDomains)) {\n violations.push({\n category: 'network outbound',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n const budgetReadPaths = budget.filesystem?.read ?? [];\n for (const entry of resolved.filesystemRead) {\n if (!isPathAllowed(entry.value, budgetReadPaths)) {\n violations.push({\n category: 'filesystem read',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n const budgetWritePaths = budget.filesystem?.write ?? [];\n for (const entry of resolved.filesystemWrite) {\n if (!isPathAllowed(entry.value, budgetWritePaths)) {\n violations.push({\n category: 'filesystem write',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n if (resolved.subprocess.length > 0 && budget.subprocess !== true) {\n violations.push({\n category: 'subprocess',\n value: 'subprocess access',\n skills: resolved.subprocess\n });\n }\n\n return violations;\n}\n\nexport function registerSkillPermissionsTool(server: McpServer): void {\n server.tool(\n 'skill-permissions',\n 'Display resolved permission summary for installed skills. Shows what capabilities each skill requires and checks against the project permission budget.',\n {\n directory: z.string().optional().describe('Project directory path (defaults to current working directory)')\n },\n async ({ directory }) => {\n const dir = directory ? path.resolve(directory) : process.cwd();\n\n if (!fs.existsSync(dir)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Directory does not exist: ${dir}`\n }\n ],\n isError: true\n };\n }\n\n let skillsJsonPath = path.join(dir, MANIFEST_FILENAME);\n if (!fs.existsSync(skillsJsonPath)) {\n skillsJsonPath = path.join(dir, LEGACY_MANIFEST_FILENAME);\n }\n if (!fs.existsSync(skillsJsonPath)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No ${MANIFEST_FILENAME} found. Run \"init-skill\" to create one.`\n }\n ],\n isError: true\n };\n }\n\n let skillsJson: SkillsJson;\n try {\n const raw = fs.readFileSync(skillsJsonPath, 'utf-8');\n skillsJson = JSON.parse(raw) as SkillsJson;\n } catch {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to parse ${path.basename(skillsJsonPath)}. The file may be corrupted.`\n }\n ],\n isError: true\n };\n }\n\n const skillDeps = skillsJson.skills ?? {};\n if (Object.keys(skillDeps).length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No skills with permissions to display. The project has no skill dependencies.'\n }\n ]\n };\n }\n\n let lockfilePath = path.join(dir, LOCKFILE_FILENAME);\n if (!fs.existsSync(lockfilePath)) {\n lockfilePath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n }\n if (!fs.existsSync(lockfilePath)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No ${LOCKFILE_FILENAME} found. Skills are declared but not installed. Run install to generate a lockfile.`\n }\n ]\n };\n }\n\n let lockfile: SkillsLock;\n try {\n const raw = fs.readFileSync(lockfilePath, 'utf-8');\n lockfile = JSON.parse(raw) as SkillsLock;\n } catch {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to parse ${path.basename(lockfilePath)}. The file may be corrupted.`\n }\n ],\n isError: true\n };\n }\n\n if (!lockfile.skills || Object.keys(lockfile.skills).length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No skills installed. The lockfile is empty.'\n }\n ]\n };\n }\n\n const resolved = collectPermissions(lockfile);\n\n const lines: string[] = [];\n lines.push('Resolved permissions for this project:');\n lines.push('');\n\n lines.push(formatSection('Network (outbound)', resolved.networkOutbound));\n lines.push(formatSection('Filesystem (read)', resolved.filesystemRead));\n lines.push(formatSection('Filesystem (write)', resolved.filesystemWrite));\n\n lines.push('Subprocess:');\n if (resolved.subprocess.length === 0) {\n lines.push(' none');\n } else {\n lines.push(` allowed ${formatAttribution(resolved.subprocess)}`);\n }\n\n if (resolved.env.length > 0) {\n lines.push(formatSection('Environment variables', resolved.env));\n }\n\n if (resolved.exec.length > 0) {\n lines.push(formatSection('Exec', resolved.exec));\n }\n\n lines.push('');\n lines.push('Per-skill breakdown:');\n for (const [key, entry] of Object.entries(lockfile.skills)) {\n const skillName = parseSkillName(key);\n const perms = entry.permissions;\n const permParts: string[] = [];\n\n if (perms.network?.outbound && perms.network.outbound.length > 0) {\n permParts.push(`network: ${perms.network.outbound.join(', ')}`);\n }\n if (perms.filesystem?.read && perms.filesystem.read.length > 0) {\n permParts.push(`filesystem:read: ${perms.filesystem.read.join(', ')}`);\n }\n if (perms.filesystem?.write && perms.filesystem.write.length > 0) {\n permParts.push(`filesystem:write: ${perms.filesystem.write.join(', ')}`);\n }\n if (perms.subprocess === true) {\n permParts.push('subprocess: allowed');\n }\n\n const rawPerms = perms as Record<string, unknown>;\n if (Array.isArray(rawPerms.env) && (rawPerms.env as string[]).length > 0) {\n permParts.push(`env: ${(rawPerms.env as string[]).join(', ')}`);\n }\n if (Array.isArray(rawPerms.exec) && (rawPerms.exec as string[]).length > 0) {\n permParts.push(`exec: ${(rawPerms.exec as string[]).join(', ')}`);\n }\n\n if (permParts.length === 0) {\n lines.push(` ${skillName}: no special permissions`);\n } else {\n lines.push(` ${skillName}: ${permParts.join('; ')}`);\n }\n }\n\n const budget = skillsJson.permissions;\n lines.push('');\n\n if (!budget) {\n lines.push('Budget status: No budget defined');\n } else {\n const violations = checkBudget(resolved, budget);\n\n if (violations.length === 0) {\n lines.push('Budget status: PASS (all within budget)');\n } else {\n lines.push('Budget status: FAIL');\n for (const v of violations) {\n lines.push(` - ${v.category}: \"${v.value}\" not in budget (requested by ${v.skills.join(', ')})`);\n }\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }]\n };\n }\n );\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\n\nexport function registerUnlinkSkillTool(server: McpServer): void {\n server.tool(\n 'unlink-skill',\n 'Unlink a skill from an agent workspace. Removes the symlink without deleting the installed skill files.',\n {\n name: z.string().describe('Skill name in @org/name format'),\n workspace: z.string().describe('Agent workspace directory path'),\n directory: z\n .string()\n .optional()\n .describe('Project directory where skills are installed (defaults to current working directory)')\n },\n async ({ name, workspace, directory }) => {\n if (!SCOPED_NAME_PATTERN.test(name)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Validation error: Skill name \"${name}\" must use the @org/name format (e.g. @acme/my-skill).`\n }\n ],\n isError: true\n };\n }\n\n const projectDir = directory ? path.resolve(directory) : process.cwd();\n const workspaceDir = path.resolve(workspace);\n\n if (!fs.existsSync(workspaceDir)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Error: Workspace directory does not exist: ${workspaceDir}`\n }\n ],\n isError: true\n };\n }\n\n const skillDir = getSkillDir(projectDir, name);\n if (!fs.existsSync(skillDir)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" is not installed. It was not found in ${skillDir}.`\n }\n ],\n isError: true\n };\n }\n\n const [scope, skillName] = name.split('/');\n const symlinkPath = path.join(workspaceDir, '.skills', scope, skillName);\n\n try {\n const stats = fs.lstatSync(symlinkPath);\n if (stats.isSymbolicLink()) {\n fs.unlinkSync(symlinkPath);\n return {\n content: [\n {\n type: 'text' as const,\n text: `Successfully unlinked \"${name}\" from ${workspaceDir}.\\nRemoved symlink: ${symlinkPath}`\n }\n ]\n };\n }\n } catch {}\n\n return {\n content: [\n {\n type: 'text' as const,\n text: `No link exists for \"${name}\" in ${workspaceDir}.`\n }\n ]\n };\n }\n );\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n MANIFEST_FILENAME,\n resolve,\n type SkillsLock\n} from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { TankApiClient } from '../lib/api-client.js';\n\nconst SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/;\n\ninterface VersionInfo {\n version: string;\n integrity: string;\n auditScore: number;\n auditStatus: string;\n publishedAt: string;\n}\n\ninterface VersionsResponse {\n name: string;\n versions: VersionInfo[];\n}\n\nfunction parseLockKey(key: string): { name: string; version: string } | null {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) return null;\n return { name: key.slice(0, lastAt), version: key.slice(lastAt + 1) };\n}\n\nexport function registerUpdateSkillTool(server: McpServer): void {\n server.tool(\n 'update-skill',\n 'Update an installed skill to the latest compatible version within its declared semver range.',\n {\n name: z.string().describe('Skill name in @org/name format'),\n directory: z.string().optional().describe('Project directory (defaults to current working directory)')\n },\n async ({ name, directory }) => {\n if (!SCOPED_NAME_PATTERN.test(name)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Validation error: Skill name \"${name}\" must use the @org/name format (e.g. @acme/my-skill).`\n }\n ],\n isError: true\n };\n }\n\n const dir = directory ? path.resolve(directory) : process.cwd();\n\n let skillsJsonPath = path.join(dir, MANIFEST_FILENAME);\n if (!fs.existsSync(skillsJsonPath)) {\n skillsJsonPath = path.join(dir, LEGACY_MANIFEST_FILENAME);\n }\n if (!fs.existsSync(skillsJsonPath)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No ${MANIFEST_FILENAME} found in ${dir}. Run the \"init-skill\" tool first.`\n }\n ],\n isError: true\n };\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(skillsJsonPath, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to read or parse ${path.basename(skillsJsonPath)}.`\n }\n ],\n isError: true\n };\n }\n\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n const versionRange = skills[name];\n\n if (!versionRange) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" is not installed (not found in ${path.basename(skillsJsonPath)}). Install it first with the install-skill tool.`\n }\n ],\n isError: true\n };\n }\n\n let lockPath = path.join(dir, LOCKFILE_FILENAME);\n if (!fs.existsSync(lockPath)) {\n lockPath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n }\n let currentVersion: string | null = null;\n\n if (fs.existsSync(lockPath)) {\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n const lock = JSON.parse(raw) as SkillsLock;\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n if (parsed.name === name) {\n currentVersion = parsed.version;\n break;\n }\n }\n } catch {\n // Lockfile unreadable — treat as no current version\n }\n }\n\n if (!currentVersion) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" is not installed (not found in ${LOCKFILE_FILENAME}). Install it first with the install-skill tool.`\n }\n ],\n isError: true\n };\n }\n\n const client = new TankApiClient();\n if (!client.isAuthenticated) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'Authentication required. Please run the \"login\" tool first to authenticate with Tank.'\n }\n ],\n isError: true\n };\n }\n\n // Fetch available versions from registry\n const encodedName = encodeURIComponent(name);\n const versionsResult = await client.fetch<VersionsResponse>(`/api/v1/skills/${encodedName}/versions`);\n\n if (!versionsResult.ok) {\n if (versionsResult.status === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Unable to connect to the Tank registry. Check your network connection and try again.`\n }\n ],\n isError: true\n };\n }\n if (versionsResult.status === 404) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" not found in the registry.`\n }\n ],\n isError: true\n };\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to fetch versions for ${name}: ${versionsResult.error}`\n }\n ],\n isError: true\n };\n }\n\n const availableVersions = versionsResult.data.versions.map((v) => v.version);\n\n // 6. Resolve best version within declared range\n const resolved = resolve(versionRange, availableVersions);\n if (!resolved) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No version of ${name} satisfies range \"${versionRange}\". Available: ${availableVersions.join(', ')}`\n }\n ],\n isError: true\n };\n }\n\n // 7. Check for newer major versions outside range\n const allMajors = availableVersions\n .map((v) => {\n const major = v.split('.')[0];\n return { version: v, major: Number.parseInt(major, 10) };\n })\n .filter((v) => !Number.isNaN(v.major));\n\n const currentMajor = Number.parseInt(currentVersion.split('.')[0], 10);\n const newerMajors = allMajors.filter((v) => v.major > currentMajor).map((v) => v.version);\n\n const highestOutOfRange =\n newerMajors.length > 0\n ? newerMajors.sort((a, b) => {\n const [aMaj, aMin, aPat] = a.split('.').map(Number);\n const [bMaj, bMin, bPat] = b.split('.').map(Number);\n return bMaj - aMaj || bMin - aMin || bPat - aPat;\n })[0]\n : null;\n\n // 8. If resolved === current, already at latest\n if (resolved === currentVersion) {\n const lines = [`Already at latest compatible version: ${name}@${resolved}`];\n if (highestOutOfRange) {\n lines.push(\n `\\nNote: Version ${highestOutOfRange} is available but outside the declared range \"${versionRange}\". Update ${MANIFEST_FILENAME} to use it.`\n );\n }\n return {\n content: [\n {\n type: 'text' as const,\n text: lines.join('')\n }\n ]\n };\n }\n\n // 9. Fetch the resolved version details for download\n const versionResult = await client.fetch<{\n version: string;\n integrity: string;\n downloadUrl: string;\n permissions: Record<string, unknown>;\n auditScore: number | null;\n }>(`/api/v1/skills/${encodedName}/${resolved}`);\n\n if (!versionResult.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to fetch version details for ${name}@${resolved}: ${versionResult.error}`\n }\n ],\n isError: true\n };\n }\n\n const versionData = versionResult.data;\n\n // 10. Download tarball\n let tarballBuffer: ArrayBuffer;\n try {\n const tarballRes = await fetch(versionData.downloadUrl, {\n headers: client.token ? { Authorization: `Bearer ${client.token}` } : {}\n });\n if (!tarballRes.ok) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to download tarball for ${name}@${resolved}: ${tarballRes.statusText}`\n }\n ],\n isError: true\n };\n }\n tarballBuffer = await tarballRes.arrayBuffer();\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Network error downloading ${name}@${resolved}: ${err instanceof Error ? err.message : String(err)}`\n }\n ],\n isError: true\n };\n }\n\n // 11. Verify SHA-512\n const { createHash } = await import('node:crypto');\n const hash = createHash('sha512').update(Buffer.from(tarballBuffer)).digest('base64');\n const computedIntegrity = `sha512-${hash}`;\n\n if (computedIntegrity !== versionData.integrity) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Integrity check failed for ${name}@${resolved}. The tarball has been tampered with or is corrupted.\\nExpected: ${versionData.integrity}\\nGot: ${computedIntegrity}`\n }\n ],\n isError: true\n };\n }\n\n // 12. Extract tarball to skill directory\n const { execSync } = await import('node:child_process');\n const skillDir = getSkillDir(dir, name);\n\n // Remove old version files if they exist\n if (fs.existsSync(skillDir)) {\n fs.rmSync(skillDir, { recursive: true, force: true });\n }\n fs.mkdirSync(skillDir, { recursive: true });\n\n const tarballPath = path.join(skillDir, '__temp_tarball.tgz');\n fs.writeFileSync(tarballPath, Buffer.from(tarballBuffer));\n\n try {\n execSync(`tar xzf \"${tarballPath}\" -C \"${skillDir}\" --strip-components=1`, {\n stdio: 'pipe'\n });\n } catch (err) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to extract tarball for ${name}@${resolved}: ${err instanceof Error ? err.message : String(err)}`\n }\n ],\n isError: true\n };\n } finally {\n // Clean up temp tarball\n try {\n fs.unlinkSync(tarballPath);\n } catch {\n /* ignore */\n }\n }\n\n // 13. Update lockfile\n let lock: SkillsLock;\n if (fs.existsSync(lockPath)) {\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n lock = { lockfileVersion: 1, skills: {} };\n }\n } else {\n lock = { lockfileVersion: 1, skills: {} };\n }\n\n // Remove old lock entry for this skill\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (parsed && parsed.name === name) {\n delete lock.skills[key];\n }\n }\n\n // Add new lock entry\n const newLockKey = `${name}@${resolved}`;\n lock.skills[newLockKey] = {\n resolved: versionData.downloadUrl,\n integrity: versionData.integrity,\n permissions: versionData.permissions as SkillsLock['skills'][string]['permissions'],\n audit_score: versionData.auditScore\n };\n\n // Sort keys for deterministic output\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n\n // 14. Build response\n const lines = [\n `Updated ${name} from ${currentVersion} to ${resolved}.`,\n `Integrity verified (SHA-512).`,\n `Lockfile updated.`\n ];\n\n if (highestOutOfRange) {\n lines.push(\n `\\nNote: Version ${highestOutOfRange} is available but outside the declared range \"${versionRange}\". Update ${MANIFEST_FILENAME} to use it.`\n );\n }\n\n return {\n content: [\n {\n type: 'text' as const,\n text: lines.join('\\n')\n }\n ]\n };\n }\n );\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { LEGACY_LOCKFILE_FILENAME, LOCKFILE_FILENAME, type SkillsLock } from '@internal/shared';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nexport function registerVerifySkillsTool(server: McpServer): void {\n server.tool(\n 'verify-skills',\n 'Verify that installed skills match their lockfile entries. Checks that skill directories exist and are not empty.',\n {\n name: z.string().optional().describe('Specific skill name to verify (verifies all if omitted)'),\n directory: z.string().optional().describe('Project directory (defaults to current working directory)')\n },\n async ({ name, directory }) => {\n const dir = directory ? path.resolve(directory) : process.cwd();\n\n let lockPath = path.join(dir, LOCKFILE_FILENAME);\n if (!fs.existsSync(lockPath)) {\n lockPath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n }\n if (!fs.existsSync(lockPath)) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `No ${LOCKFILE_FILENAME} found. Run \"install-skill\" to install skills and generate a lockfile.`\n }\n ],\n isError: true\n };\n }\n\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to parse ${path.basename(lockPath)}. The file may be corrupted.`\n }\n ],\n isError: true\n };\n }\n\n let entries = Object.entries(lock.skills);\n\n if (entries.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No skills to verify. The lockfile is empty.'\n }\n ]\n };\n }\n\n if (name) {\n entries = entries.filter(([key]) => {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) return false;\n return key.slice(0, lastAt) === name;\n });\n\n if (entries.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Skill \"${name}\" not found in lockfile.`\n }\n ],\n isError: true\n };\n }\n }\n\n const results: Array<{ key: string; status: 'PASS' | 'FAIL' | 'MISSING'; detail: string }> = [];\n\n for (const [key, entry] of entries) {\n const skillName = parseLockKey(key);\n const skillDir = getExtractDir(dir, skillName);\n\n if (!fs.existsSync(skillDir)) {\n results.push({\n key,\n status: 'MISSING',\n detail: `Directory missing at ${skillDir}. Reinstall with \"install-skill\".`\n });\n continue;\n }\n\n const contents = fs.readdirSync(skillDir);\n if (contents.length === 0) {\n results.push({\n key,\n status: 'FAIL',\n detail: `Directory exists but is empty. Expected integrity: ${entry.integrity}. SHA-512 mismatch detected.`\n });\n continue;\n }\n\n results.push({\n key,\n status: 'PASS',\n detail: `Verified (integrity: ${entry.integrity})`\n });\n }\n\n const passing = results.filter((r) => r.status === 'PASS');\n const failing = results.filter((r) => r.status !== 'PASS');\n\n const lines: string[] = [];\n for (const r of results) {\n lines.push(`${r.status} ${r.key}: ${r.detail}`);\n }\n\n if (failing.length > 0) {\n lines.push('');\n lines.push(`Verification failed: ${failing.length} issue(s) found, ${passing.length} passed.`);\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }],\n isError: true\n };\n }\n\n lines.push('');\n lines.push(`All ${passing.length} skill(s) passed verification.`);\n return {\n content: [{ type: 'text' as const, text: lines.join('\\n') }]\n };\n }\n );\n}\n\nfunction parseLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) return key;\n return key.slice(0, lastAt);\n}\n\nfunction getExtractDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { TankApiClient } from '../lib/api-client.js';\n\nexport function registerWhoamiTool(server: McpServer): void {\n server.tool('whoami', 'Show the authenticated Tank user for the current local session.', {}, async () => {\n const client = new TankApiClient();\n\n if (!client.isAuthenticated) {\n return {\n content: [{ type: 'text' as const, text: 'Not logged in. Use the login tool to authenticate.' }]\n };\n }\n\n const authCheck = await client.verifyAuth();\n\n if (authCheck.valid) {\n const name = authCheck.user.name ?? 'unknown';\n const email = authCheck.user.email ?? 'unknown';\n return {\n content: [{ type: 'text' as const, text: `Logged in as ${name}\\nEmail: ${email}` }]\n };\n }\n\n if (authCheck.reason === 'network-error') {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to connect to the registry. Check your network connection.\\nError: ${authCheck.error ?? 'unknown'}`\n }\n ],\n isError: true\n };\n }\n\n return {\n content: [{ type: 'text' as const, text: 'Session expired or invalid. Use the login tool to re-authenticate.' }]\n };\n });\n}\n","#!/usr/bin/env node\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { registerAuditSkillTool } from './tools/audit-skill.js';\nimport { registerDoctorTool } from './tools/doctor.js';\nimport { registerInitSkillTool } from './tools/init-skill.js';\nimport { registerInstallSkillTool } from './tools/install-skill.js';\nimport { registerLinkSkillTool } from './tools/link-skill.js';\n// Import tools\nimport { registerLoginTool } from './tools/login.js';\nimport { registerLogoutTool } from './tools/logout.js';\nimport { registerPublishSkillTool } from './tools/publish-skill.js';\nimport { registerRemoveSkillTool } from './tools/remove-skill.js';\nimport { registerScanSkillTool } from './tools/scan-skill.js';\nimport { registerSearchSkillsTool } from './tools/search-skills.js';\nimport { registerSkillInfoTool } from './tools/skill-info.js';\nimport { registerSkillPermissionsTool } from './tools/skill-permissions.js';\nimport { registerUnlinkSkillTool } from './tools/unlink-skill.js';\nimport { registerUpdateSkillTool } from './tools/update-skill.js';\nimport { registerVerifySkillsTool } from './tools/verify-skills.js';\nimport { registerWhoamiTool } from './tools/whoami.js';\n\n// Create MCP server instance\nconst server = new McpServer({\n name: 'tank',\n version: '0.1.0'\n});\n\n// Register all tools\nregisterLoginTool(server);\nregisterSearchSkillsTool(server);\nregisterSkillInfoTool(server);\nregisterScanSkillTool(server);\nregisterPublishSkillTool(server);\nregisterLogoutTool(server);\nregisterWhoamiTool(server);\nregisterInitSkillTool(server);\nregisterRemoveSkillTool(server);\nregisterVerifySkillsTool(server);\nregisterLinkSkillTool(server);\nregisterUnlinkSkillTool(server);\nregisterDoctorTool(server);\nregisterSkillPermissionsTool(server);\nregisterInstallSkillTool(server);\nregisterUpdateSkillTool(server);\nregisterAuditSkillTool(server);\n\n// Start stdio transport\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((error) => {\n console.error('MCP server error:', error);\n process.exit(1);\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tankpkg/mcp-server",
3
- "version": "0.6.3",
3
+ "version": "0.8.1",
4
4
  "description": "MCP server for Tank - scan and publish AI agent skills from your editor",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,16 +12,28 @@
12
12
  "publishConfig": {
13
13
  "access": "public"
14
14
  },
15
+ "scripts": {
16
+ "build": "tsdown",
17
+ "dev": "tsx --watch src/index.ts",
18
+ "start": "node dist/index.js",
19
+ "test": "vitest run",
20
+ "lint": "biome check src/",
21
+ "lint:fix": "biome check --fix src/",
22
+ "format": "biome format --write src/"
23
+ },
15
24
  "devDependencies": {
16
- "@types/node": "^22.19.11",
17
- "vitest": "^3"
25
+ "@internal/shared": "workspace:*",
26
+ "@types/node": "^25",
27
+ "tsdown": "^0.21.0",
28
+ "tsx": "^4.19.0",
29
+ "vitest": "^4"
18
30
  },
19
31
  "dependencies": {
20
- "@modelcontextprotocol/sdk": "^1.12.1",
32
+ "@modelcontextprotocol/sdk": "^1.27.1",
21
33
  "ignore": "^7.0.5",
34
+ "semver": "^7.7.4",
22
35
  "tar": "^7.5.8",
23
- "zod": "^4.3.6",
24
- "@tank/shared": "0.1.0"
36
+ "zod": "^4.3.6"
25
37
  },
26
38
  "keywords": [
27
39
  "mcp",
@@ -37,11 +49,5 @@
37
49
  "url": "https://github.com/janwilken/tank.git",
38
50
  "directory": "packages/mcp-server"
39
51
  },
40
- "license": "MIT",
41
- "scripts": {
42
- "build": "tsc",
43
- "dev": "tsc --watch",
44
- "start": "node dist/index.js",
45
- "test": "vitest run"
46
- }
47
- }
52
+ "license": "MIT"
53
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Tank Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -1,45 +0,0 @@
1
- export interface ApiClientOptions {
2
- configDir?: string;
3
- }
4
- /**
5
- * Tank API client for MCP server.
6
- */
7
- export declare class TankApiClient {
8
- private config;
9
- constructor(options?: ApiClientOptions);
10
- /**
11
- * Get the base URL for the Tank API.
12
- */
13
- get baseUrl(): string;
14
- /**
15
- * Get the auth token (if available).
16
- */
17
- get token(): string | undefined;
18
- /**
19
- * Check if authenticated.
20
- */
21
- get isAuthenticated(): boolean;
22
- /**
23
- * Make an authenticated API request.
24
- */
25
- fetch<T>(path: string, options?: RequestInit): Promise<{
26
- data: T;
27
- ok: true;
28
- } | {
29
- error: string;
30
- status: number;
31
- ok: false;
32
- }>;
33
- verifyAuth(): Promise<{
34
- valid: true;
35
- user: {
36
- name: string | null;
37
- email: string | null;
38
- };
39
- } | {
40
- valid: false;
41
- reason: 'no-token' | 'unauthorized' | 'network-error';
42
- error?: string;
43
- }>;
44
- }
45
- //# sourceMappingURL=api-client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAa;gBAEf,OAAO,GAAE,gBAAqB;IAI1C;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED;;OAEG;IACG,KAAK,CAAC,CAAC,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,KAAK,CAAA;KAAE,CAAC;IAqC1E,UAAU,IAAI,OAAO,CACvB;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;KAAE,GACpE;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,eAAe,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAC1F;CAmBF"}