@withpica/mcp-server-directory 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +43 -26
  2. package/package.json +4 -2
  3. package/scripts/build-skills.ts +192 -0
  4. package/src/__tests__/prompts/index.test.ts +47 -64
  5. package/src/__tests__/prompts/prompt-eval-harness.test.ts +135 -104
  6. package/src/__tests__/skills/skills-registry.test.ts +63 -0
  7. package/src/__tests__/skills/skills-tools.test.ts +74 -0
  8. package/src/__tests__/tools/chain.test.ts +122 -0
  9. package/src/__tests__/tools/composability-chains.test.ts +4 -2
  10. package/src/__tests__/tools/people.test.ts +9 -3
  11. package/src/__tests__/tools/works.test.ts +32 -3
  12. package/src/prompts/index.ts +97 -141
  13. package/src/prompts/public-question-atlas.ts +557 -0
  14. package/src/server.ts +123 -11
  15. package/src/skills/find-music-for-sync-brief/SKILL.md +89 -0
  16. package/src/skills/index.ts +63 -0
  17. package/src/skills/skills.generated.ts +33 -0
  18. package/src/tools/chain.ts +118 -0
  19. package/src/tools/index.ts +15 -0
  20. package/src/tools/people.ts +22 -41
  21. package/src/tools/recordings.ts +7 -3
  22. package/src/tools/search.ts +7 -4
  23. package/src/tools/skills.ts +146 -0
  24. package/src/tools/works.ts +39 -46
  25. package/dist/client.d.ts +0 -12
  26. package/dist/client.d.ts.map +0 -1
  27. package/dist/client.js +0 -93
  28. package/dist/client.js.map +0 -1
  29. package/dist/config.d.ts +0 -9
  30. package/dist/config.d.ts.map +0 -1
  31. package/dist/config.js +0 -15
  32. package/dist/config.js.map +0 -1
  33. package/dist/index.d.ts +0 -3
  34. package/dist/index.d.ts.map +0 -1
  35. package/dist/index.js +0 -28
  36. package/dist/index.js.map +0 -1
  37. package/dist/prompts/index.d.ts +0 -33
  38. package/dist/prompts/index.d.ts.map +0 -1
  39. package/dist/prompts/index.js +0 -208
  40. package/dist/prompts/index.js.map +0 -1
  41. package/dist/resources/llms-primer.d.ts +0 -2
  42. package/dist/resources/llms-primer.d.ts.map +0 -1
  43. package/dist/resources/llms-primer.js +0 -35
  44. package/dist/resources/llms-primer.js.map +0 -1
  45. package/dist/server.d.ts +0 -13
  46. package/dist/server.d.ts.map +0 -1
  47. package/dist/server.js +0 -104
  48. package/dist/server.js.map +0 -1
  49. package/dist/tools/index.d.ts +0 -26
  50. package/dist/tools/index.d.ts.map +0 -1
  51. package/dist/tools/index.js +0 -43
  52. package/dist/tools/index.js.map +0 -1
  53. package/dist/tools/people.d.ts +0 -14
  54. package/dist/tools/people.d.ts.map +0 -1
  55. package/dist/tools/people.js +0 -190
  56. package/dist/tools/people.js.map +0 -1
  57. package/dist/tools/recordings.d.ts +0 -12
  58. package/dist/tools/recordings.d.ts.map +0 -1
  59. package/dist/tools/recordings.js +0 -141
  60. package/dist/tools/recordings.js.map +0 -1
  61. package/dist/tools/search.d.ts +0 -12
  62. package/dist/tools/search.d.ts.map +0 -1
  63. package/dist/tools/search.js +0 -53
  64. package/dist/tools/search.js.map +0 -1
  65. package/dist/tools/works.d.ts +0 -15
  66. package/dist/tools/works.d.ts.map +0 -1
  67. package/dist/tools/works.js +0 -249
  68. package/dist/tools/works.js.map +0 -1
  69. package/dist/utils/errors.d.ts +0 -14
  70. package/dist/utils/errors.d.ts.map +0 -1
  71. package/dist/utils/errors.js +0 -48
  72. package/dist/utils/errors.js.map +0 -1
  73. package/dist/utils/formatting.d.ts +0 -10
  74. package/dist/utils/formatting.d.ts.map +0 -1
  75. package/dist/utils/formatting.js +0 -16
  76. package/dist/utils/formatting.js.map +0 -1
@@ -16,12 +16,16 @@ export class DirectoryRecordingsTools {
16
16
  {
17
17
  definition: {
18
18
  name: "directory_search_recordings",
19
+ tier: "read",
19
20
  description:
20
- "Search recordings across the PICA directory by audio characteristics. " +
21
+ "Use when the user asks: 'find upbeat tracks around 120 BPM', " +
22
+ "'find instrumental tracks in C minor', 'find energetic music for sync', " +
23
+ "'tracks at X BPM in [key]'. " +
24
+ "AUDIO-FEATURE search only — for name- or title-shaped queries use directory_lookup_work, " +
25
+ "directory_lookup_isrc, or directory_lookup_person, NOT this tool. " +
21
26
  "Find tracks by BPM, key, energy, danceability, duration, and more. " +
22
27
  "Only returns recordings from organisations that have opted into the directory. " +
23
- "Use this for sync licensing searches like 'find upbeat tracks around 120 BPM in a minor key'. " +
24
- "→ then: directory_lookup_work_full (credits and licensing details for a matched track)",
28
+ "→ then: directory_lookup_work (credits and licensing details for a matched track)",
25
29
  inputSchema: {
26
30
  type: "object",
27
31
  properties: {
@@ -16,11 +16,14 @@ export class DirectorySearchTools {
16
16
  {
17
17
  definition: {
18
18
  name: "directory_search",
19
+ tier: "read",
19
20
  description:
20
- "Search across all verified works and creators in the PICA public directory. " +
21
- "Returns mixed results sorted by relevance. Use this for broad searches; " +
22
- "use list_works or list_people for filtered browsing. " +
23
- "→ then: directory_lookup_work_full (work details), directory_lookup_person_full (creator details)",
21
+ "Use when the user asks: 'find me X', 'who or what is X?', " +
22
+ "'find any record of X'. " +
23
+ "Use when the entity type is not yet known. " +
24
+ "Convenience fan-out across works and creators — runs list_works and list_people with the same query and returns the union. " +
25
+ "No cross-type ranking; prefer list_works or list_people when the entity type is known. " +
26
+ "→ then: directory_lookup_work (work details), directory_lookup_person (creator details)",
24
27
  inputSchema: {
25
28
  type: "object",
26
29
  properties: {
@@ -0,0 +1,146 @@
1
+ // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
+
3
+ /**
4
+ * Directory MCP skill tools — ADR-140 Phase 2b
5
+ *
6
+ * Sister of mcp-server/src/tools/skills.ts. Two meta-tools — list and get —
7
+ * named `directory_skill_*` to match the directory MCP's `directory_*` prefix
8
+ * convention. Same lazy-loading shape as the creator MCP variant.
9
+ */
10
+
11
+ import { ToolDefinition, ToolExecutor, ToolResult } from "./index.js";
12
+ import { SkillsRegistry, SkillNotFoundError } from "../skills/index.js";
13
+
14
+ export class DirectorySkillsTools {
15
+ private registry: SkillsRegistry;
16
+
17
+ constructor() {
18
+ this.registry = new SkillsRegistry();
19
+ }
20
+
21
+ getTools(): Array<{ definition: ToolDefinition; executor: ToolExecutor }> {
22
+ return [
23
+ {
24
+ definition: {
25
+ name: "directory_skill_list",
26
+ tier: "read",
27
+ description:
28
+ "Use when the user asks: 'what skills do you have?', 'what can you do?', 'show me your methodologies', 'how do I find music here?'. " +
29
+ "Returns the list of downloadable directory skill methodologies — name, description, trigger phrases. " +
30
+ "Cheap to call. Use directory_skill_get(name) to load a specific skill's full methodology body.",
31
+ inputSchema: {
32
+ type: "object",
33
+ properties: {},
34
+ additionalProperties: false,
35
+ },
36
+ },
37
+ executor: this.listSkills.bind(this),
38
+ },
39
+ {
40
+ definition: {
41
+ name: "directory_skill_get",
42
+ tier: "read",
43
+ description:
44
+ "Use when the user asks: 'what skills do you have?', 'what can you do?', 'show me your methodologies'. " +
45
+ "Returns the full methodology body for one directory skill — markdown with step-by-step instructions and tool chains. " +
46
+ "Call directory_skill_list first to see available skill names, then directory_skill_get to load one. Read once at the start of the workflow; do not re-fetch on every step.",
47
+ inputSchema: {
48
+ type: "object",
49
+ properties: {
50
+ name: {
51
+ type: "string",
52
+ description:
53
+ "Kebab-case skill name from directory_skill_list (e.g. 'find-music-for-sync-brief').",
54
+ },
55
+ },
56
+ required: ["name"],
57
+ additionalProperties: false,
58
+ },
59
+ },
60
+ executor: this.getSkill.bind(this),
61
+ },
62
+ ];
63
+ }
64
+
65
+ private async listSkills(_args: Record<string, any>): Promise<ToolResult> {
66
+ const skills = this.registry.listSkills();
67
+ return {
68
+ content: [
69
+ {
70
+ type: "text",
71
+ text: skills.length
72
+ ? `Available directory skills (${skills.length}):\n\n` +
73
+ skills
74
+ .map(
75
+ (s) =>
76
+ `- ${s.name} — ${s.description}\n triggers: ${s.triggers.join(", ")}\n audience: ${s.audience}`,
77
+ )
78
+ .join("\n\n") +
79
+ "\n\nCall directory_skill_get(name) to load a skill's full methodology."
80
+ : "No directory skills available.",
81
+ },
82
+ ],
83
+ structuredContent: { skills },
84
+ };
85
+ }
86
+
87
+ private async getSkill(args: Record<string, any>): Promise<ToolResult> {
88
+ const name = String(args?.name ?? "").trim();
89
+ if (!name) {
90
+ return {
91
+ isError: true,
92
+ content: [
93
+ {
94
+ type: "text",
95
+ text: "Missing required argument: name. Call directory_skill_list to see available skill names.",
96
+ },
97
+ ],
98
+ };
99
+ }
100
+ let skill;
101
+ try {
102
+ skill = this.registry.getSkill(name);
103
+ } catch (err) {
104
+ if (err instanceof SkillNotFoundError) {
105
+ return {
106
+ isError: true,
107
+ content: [
108
+ {
109
+ type: "text",
110
+ text: `Skill not found: ${name}. Call directory_skill_list to see available skill names.`,
111
+ },
112
+ ],
113
+ };
114
+ }
115
+ throw err;
116
+ }
117
+ const frontmatterSummary = [
118
+ `# ${skill.name}`,
119
+ "",
120
+ `**Description:** ${skill.description}`,
121
+ `**Audience:** ${skill.audience}`,
122
+ `**Triggers:** ${skill.triggers.join(", ")}`,
123
+ `**Tools used:** ${skill.tools_required.join(", ")}`,
124
+ `**Output:** ${skill.output}`,
125
+ "",
126
+ "---",
127
+ "",
128
+ ].join("\n");
129
+ return {
130
+ content: [
131
+ {
132
+ type: "text",
133
+ text: frontmatterSummary + skill.body,
134
+ },
135
+ ],
136
+ structuredContent: {
137
+ name: skill.name,
138
+ description: skill.description,
139
+ triggers: skill.triggers,
140
+ audience: skill.audience,
141
+ tools_required: skill.tools_required,
142
+ output: skill.output,
143
+ },
144
+ };
145
+ }
146
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { DirectoryClient } from "../client.js";
4
4
  import { ToolDefinition, ToolExecutor } from "./index.js";
5
- import { formatAsText, formatStructuredList } from "../utils/formatting.js";
5
+ import { formatStructuredList } from "../utils/formatting.js";
6
6
 
7
7
  export class DirectoryWorksTools {
8
8
  private client: DirectoryClient;
@@ -16,10 +16,14 @@ export class DirectoryWorksTools {
16
16
  {
17
17
  definition: {
18
18
  name: "directory_list_works",
19
+ tier: "read",
19
20
  description:
21
+ "Use when the user asks: 'list works in PICA', 'works starting with B', " +
22
+ "'find works by publisher X', 'browse the catalog'. " +
20
23
  "Browse and filter verified musical works in the PICA public directory. " +
21
- "Search by title, ISWC, publisher, or label. Returns paginated results. " +
22
- "→ then: directory_lookup_work_full (full details with credits and audio)",
24
+ "Filter by free-text (q), ISWC, ISRC, publisher, label, or starting letter (A–Z, or '#' for numeric). " +
25
+ "Sort by title (default) or recent. Returns paginated results. " +
26
+ "→ then: directory_lookup_work (full details with credits and audio)",
23
27
  inputSchema: {
24
28
  type: "object",
25
29
  properties: {
@@ -31,6 +35,11 @@ export class DirectoryWorksTools {
31
35
  type: "string",
32
36
  description: "Filter by ISWC (e.g. T-123.456.789-0)",
33
37
  },
38
+ isrc: {
39
+ type: "string",
40
+ description:
41
+ "Filter by ISRC (exact match, cross-table recording lookup)",
42
+ },
34
43
  publisher: {
35
44
  type: "string",
36
45
  description: "Filter by publisher name (partial match)",
@@ -39,6 +48,16 @@ export class DirectoryWorksTools {
39
48
  type: "string",
40
49
  description: "Filter by record label (partial match)",
41
50
  },
51
+ letter: {
52
+ type: "string",
53
+ description:
54
+ "Filter by title starting letter (A–Z), or '#' for numeric",
55
+ },
56
+ sort: {
57
+ type: "string",
58
+ enum: ["title", "recent"],
59
+ description: "Sort order (default: title)",
60
+ },
42
61
  page: {
43
62
  type: "number",
44
63
  description: "Page number (default: 1, 20 results per page)",
@@ -51,10 +70,15 @@ export class DirectoryWorksTools {
51
70
  {
52
71
  definition: {
53
72
  name: "directory_lookup_work",
73
+ tier: "read",
54
74
  description:
55
- "Get full details of a single work by ISWC or work ID. " +
56
- "Returns credits, recordings, attestation status, provenance, DSP links, and carbon badge. " +
57
- "→ then: directory_lookup_work_full (agent-optimised markdown), directory_lookup_person_full (inspect a credited writer)",
75
+ "Use when the user asks: 'what's the ISWC for X?', 'who wrote X?', " +
76
+ "'tell me about song X', 'is this song registered?'. " +
77
+ "Get full details of a single work by ISWC or work UUID. " +
78
+ "Returns credits with IPI numbers and attestation status, recordings with ISRCs, " +
79
+ "audio analysis (BPM, key, energy), DSP links (Spotify, Apple Music), " +
80
+ "AI provenance, and registration score as a structured markdown summary. " +
81
+ "→ then: directory_lookup_person (research a credited writer), directory_search_recordings (find similar tracks by audio)",
58
82
  inputSchema: {
59
83
  type: "object",
60
84
  properties: {
@@ -68,34 +92,16 @@ export class DirectoryWorksTools {
68
92
  },
69
93
  executor: this.lookupWork.bind(this),
70
94
  },
71
- {
72
- definition: {
73
- name: "directory_lookup_work_full",
74
- description:
75
- "Get complete details about a work in the public directory — credits with IPI numbers and attestation status, " +
76
- "recordings with ISRCs, audio analysis (BPM, key, energy), DSP links (Spotify, Apple Music), " +
77
- "AI provenance, and registration score. Returns a structured markdown summary optimised for agent reasoning. " +
78
- "→ then: directory_lookup_person_full (research a credited writer), directory_search_recordings (find similar tracks by audio)",
79
- inputSchema: {
80
- type: "object",
81
- properties: {
82
- identifier: {
83
- type: "string",
84
- description: "ISWC (e.g. T-123.456.789-0) or work UUID",
85
- },
86
- },
87
- required: ["identifier"],
88
- },
89
- },
90
- executor: this.lookupWorkFull.bind(this),
91
- },
92
95
  {
93
96
  definition: {
94
97
  name: "directory_lookup_isrc",
98
+ tier: "read",
95
99
  description:
96
- "Find musical work(s) associated with an ISRC code. " +
97
- "ISRC identifies a specific recording; this returns the underlying work(s). " +
98
- "→ then: directory_lookup_work_full (full details on matched work)",
100
+ "Use when the user asks: 'lookup ISRC X', 'what's the work for ISRC X?', " +
101
+ "'find the work for this recording'. " +
102
+ "Shortcut: look up the work(s) associated with a recording identifier (ISRC). " +
103
+ "Equivalent to directory_list_works with an isrc filter, pinned for recording-first discovery. " +
104
+ "→ then: directory_lookup_work (full details on the matched work)",
99
105
  inputSchema: {
100
106
  type: "object",
101
107
  properties: {
@@ -124,8 +130,11 @@ export class DirectoryWorksTools {
124
130
 
125
131
  if (args.q) params.q = args.q;
126
132
  if (args.iswc) params.iswc = args.iswc;
133
+ if (args.isrc) params.isrc = args.isrc;
127
134
  if (args.publisher) params.publisher = args.publisher;
128
135
  if (args.label) params.label = args.label;
136
+ if (args.letter) params.letter = args.letter;
137
+ if (args.sort) params.sort = args.sort;
129
138
 
130
139
  const response: any = await this.client.request("/works", params);
131
140
 
@@ -141,14 +150,6 @@ export class DirectoryWorksTools {
141
150
  `/works/${encodeURIComponent(args.identifier)}`,
142
151
  );
143
152
 
144
- return formatAsText(response.data);
145
- }
146
-
147
- private async lookupWorkFull(args: Record<string, any>): Promise<any> {
148
- const response: any = await this.client.request(
149
- `/works/${encodeURIComponent(args.identifier)}`,
150
- );
151
-
152
153
  const data = response.data;
153
154
  if (!data) {
154
155
  return {
@@ -159,19 +160,16 @@ export class DirectoryWorksTools {
159
160
 
160
161
  const lines: string[] = [];
161
162
 
162
- // Title + artist
163
163
  const artistName = data.artist_name || data.primary_artist || "";
164
164
  lines.push(`## ${data.title}${artistName ? ` by ${artistName}` : ""}`);
165
165
  lines.push("");
166
166
 
167
- // Key metadata line
168
167
  const metaParts: string[] = [];
169
168
  if (data.iswc) metaParts.push(`**ISWC:** ${data.iswc}`);
170
169
  if (data.organisation_name)
171
170
  metaParts.push(`**Organisation:** ${data.organisation_name}`);
172
171
  if (metaParts.length > 0) lines.push(metaParts.join(" | "));
173
172
 
174
- // Score
175
173
  if (data.score !== undefined && data.score !== null) {
176
174
  const tier =
177
175
  data.score >= 80
@@ -184,7 +182,6 @@ export class DirectoryWorksTools {
184
182
 
185
183
  lines.push("");
186
184
 
187
- // Credits
188
185
  const credits: any[] = data.credits || [];
189
186
  lines.push(`### Credits (${credits.length})`);
190
187
  if (credits.length > 0) {
@@ -202,7 +199,6 @@ export class DirectoryWorksTools {
202
199
  }
203
200
  lines.push("");
204
201
 
205
- // Recordings
206
202
  const recordings: any[] = data.recordings || [];
207
203
  lines.push(`### Recordings (${recordings.length})`);
208
204
  if (recordings.length > 0) {
@@ -218,7 +214,6 @@ export class DirectoryWorksTools {
218
214
  }
219
215
  lines.push("");
220
216
 
221
- // Audio analysis
222
217
  const audio = data.audio_analysis || data.audio || null;
223
218
  if (audio) {
224
219
  lines.push("### Audio");
@@ -234,14 +229,12 @@ export class DirectoryWorksTools {
234
229
  lines.push("");
235
230
  }
236
231
 
237
- // DSP links
238
232
  const dsp = data.dsp_links || data.streaming_links || null;
239
233
  if (dsp && typeof dsp === "object" && Object.keys(dsp).length > 0) {
240
234
  lines.push("### DSP Links");
241
235
  if (dsp.spotify) lines.push(`- Spotify: ${dsp.spotify}`);
242
236
  if (dsp.apple_music) lines.push(`- Apple Music: ${dsp.apple_music}`);
243
237
  if (dsp.youtube) lines.push(`- YouTube: ${dsp.youtube}`);
244
- // catch any other keys
245
238
  for (const [key, val] of Object.entries(dsp)) {
246
239
  if (!["spotify", "apple_music", "youtube"].includes(key) && val) {
247
240
  lines.push(`- ${key}: ${val}`);
package/dist/client.d.ts DELETED
@@ -1,12 +0,0 @@
1
- export interface DirectoryClientConfig {
2
- baseUrl: string;
3
- debug?: boolean;
4
- }
5
- export declare class DirectoryClient {
6
- private baseUrl;
7
- private debug;
8
- constructor(config: DirectoryClientConfig);
9
- private fetchWithTimeout;
10
- request<T>(path: string, params?: Record<string, string>): Promise<T>;
11
- }
12
- //# sourceMappingURL=client.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAU;gBAEX,MAAM,EAAE,qBAAqB;YAK3B,gBAAgB;IAmBxB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAgF5E"}
package/dist/client.js DELETED
@@ -1,93 +0,0 @@
1
- // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
- const DEFAULT_TIMEOUT_MS = 15_000;
3
- const MAX_RETRIES = 2;
4
- const RETRY_BASE_MS = 500;
5
- function isRetryableStatus(status) {
6
- return status === 429 || status === 502 || status === 503 || status === 504;
7
- }
8
- function sleep(ms) {
9
- return new Promise((resolve) => setTimeout(resolve, ms));
10
- }
11
- export class DirectoryClient {
12
- baseUrl;
13
- debug;
14
- constructor(config) {
15
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
16
- this.debug = config.debug || false;
17
- }
18
- async fetchWithTimeout(url, init, timeoutMs) {
19
- const controller = new AbortController();
20
- const timer = setTimeout(() => controller.abort(), timeoutMs);
21
- try {
22
- return await fetch(url, { ...init, signal: controller.signal });
23
- }
24
- catch (err) {
25
- if (err.name === "AbortError") {
26
- throw new Error(`Request timed out after ${timeoutMs}ms: GET ${url}`);
27
- }
28
- throw err;
29
- }
30
- finally {
31
- clearTimeout(timer);
32
- }
33
- }
34
- async request(path, params) {
35
- const url = new URL(`${this.baseUrl}/api/public/directory${path}`);
36
- if (params) {
37
- Object.entries(params).forEach(([key, value]) => {
38
- if (value !== undefined && value !== "") {
39
- url.searchParams.set(key, value);
40
- }
41
- });
42
- }
43
- if (this.debug) {
44
- console.error(`[Directory MCP] GET ${url.toString()} (timeout: ${DEFAULT_TIMEOUT_MS}ms)`);
45
- }
46
- let lastError;
47
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
48
- try {
49
- const response = await this.fetchWithTimeout(url.toString(), { method: "GET", headers: { Accept: "application/json" } }, DEFAULT_TIMEOUT_MS);
50
- if (response.ok) {
51
- const json = await response.json();
52
- if (json.success === false) {
53
- throw new Error(json.error || "API returned unsuccessful response");
54
- }
55
- return json;
56
- }
57
- const errorText = await response.text();
58
- // Auth errors — fail fast, never retry
59
- if (response.status === 401 || response.status === 403) {
60
- throw new Error(`Authentication failed: ${response.status} ${errorText}`);
61
- }
62
- if (isRetryableStatus(response.status) && attempt < MAX_RETRIES) {
63
- const retryAfter = response.headers.get("retry-after");
64
- const parsed = retryAfter ? parseInt(retryAfter, 10) : NaN;
65
- const delayMs = !isNaN(parsed)
66
- ? parsed * 1000
67
- : RETRY_BASE_MS * Math.pow(2, attempt);
68
- if (this.debug) {
69
- console.error(`[Directory MCP] ${response.status} on attempt ${attempt + 1}, retrying in ${delayMs}ms`);
70
- }
71
- await sleep(delayMs);
72
- continue;
73
- }
74
- throw new Error(`API request failed: ${response.status} ${errorText}`);
75
- }
76
- catch (err) {
77
- lastError = err;
78
- if (err.message?.includes("timed out") &&
79
- attempt < MAX_RETRIES) {
80
- const delayMs = RETRY_BASE_MS * Math.pow(2, attempt);
81
- if (this.debug) {
82
- console.error(`[Directory MCP] timeout on attempt ${attempt + 1}, retrying in ${delayMs}ms`);
83
- }
84
- await sleep(delayMs);
85
- continue;
86
- }
87
- throw err;
88
- }
89
- }
90
- throw lastError || new Error("Request failed after retries");
91
- }
92
- }
93
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts DELETED
@@ -1,9 +0,0 @@
1
- export interface ServerConfig {
2
- directoryUrl: string;
3
- serverName: string;
4
- version: string;
5
- debug: boolean;
6
- }
7
- export declare function loadConfig(): ServerConfig;
8
- export declare function validateConfig(config: ServerConfig): void;
9
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,UAAU,IAAI,YAAY,CAOzC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAIzD"}
package/dist/config.js DELETED
@@ -1,15 +0,0 @@
1
- // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
2
- export function loadConfig() {
3
- return {
4
- directoryUrl: process.env.PICA_DIRECTORY_URL || "https://withpica.com",
5
- serverName: "pica-directory-mcp-server",
6
- version: "1.0.0",
7
- debug: process.env.DEBUG === "true" || process.env.DEBUG === "1",
8
- };
9
- }
10
- export function validateConfig(config) {
11
- if (!config.directoryUrl || !config.directoryUrl.startsWith("http")) {
12
- throw new Error("Invalid PICA_DIRECTORY_URL");
13
- }
14
- }
15
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAS7D,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,sBAAsB;QACtE,UAAU,EAAE,2BAA2B;QACvC,OAAO,EAAE,OAAO;QAChB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAoB;IACjD,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
package/dist/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js DELETED
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2024-2026 Withpica Ltd. All rights reserved.
3
- import { loadConfig, validateConfig } from "./config.js";
4
- import { DirectoryMcpServer } from "./server.js";
5
- async function main() {
6
- try {
7
- const config = loadConfig();
8
- validateConfig(config);
9
- const server = new DirectoryMcpServer(config);
10
- await server.start();
11
- process.on("SIGINT", async () => {
12
- console.error("[Directory MCP] Shutting down...");
13
- await server.stop();
14
- process.exit(0);
15
- });
16
- process.on("SIGTERM", async () => {
17
- console.error("[Directory MCP] Shutting down...");
18
- await server.stop();
19
- process.exit(0);
20
- });
21
- }
22
- catch (error) {
23
- console.error("[Directory MCP] Fatal error:", error instanceof Error ? error.message : String(error));
24
- process.exit(1);
25
- }
26
- }
27
- main();
28
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,6DAA6D;AAG7D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,cAAc,CAAC,MAAM,CAAC,CAAC;QAEvB,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QAErB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;YAClD,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;YAC/B,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;YAClD,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CACX,8BAA8B,EAC9B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -1,33 +0,0 @@
1
- /**
2
- * Skill Registry for Directory MCP Server
3
- * Workflow-level skills for music discovery and creator research.
4
- * Deployed as upgraded prompts; will convert to MCP skills format
5
- * when the spec extension ships (ADR-171).
6
- */
7
- export interface PromptDefinition {
8
- name: string;
9
- description: string;
10
- arguments?: Array<{
11
- name: string;
12
- description: string;
13
- required?: boolean;
14
- }>;
15
- }
16
- export interface PromptMessage {
17
- role: "user" | "assistant";
18
- content: {
19
- type: string;
20
- text: string;
21
- };
22
- }
23
- export interface PromptResult {
24
- messages: PromptMessage[];
25
- [key: string]: unknown;
26
- }
27
- export declare class PromptRegistry {
28
- listPrompts(): PromptDefinition[];
29
- getPrompt(name: string, args?: Record<string, any>): Promise<PromptResult>;
30
- private getDiscoverMusicSkill;
31
- private getResearchCreatorSkill;
32
- }
33
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAkBD,qBAAa,cAAc;IACzB,WAAW,IAAI,gBAAgB,EAAE;IA8B3B,SAAS,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACzB,OAAO,CAAC,YAAY,CAAC;IAWxB,OAAO,CAAC,qBAAqB;IAiF7B,OAAO,CAAC,uBAAuB;CA2EhC"}