@coldiq/mcp 0.3.15 → 0.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/skills.d.ts +20 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +89 -0
- package/dist/skills.js.map +1 -0
- package/dist/tools/list-skills.d.ts +17 -0
- package/dist/tools/list-skills.d.ts.map +1 -0
- package/dist/tools/list-skills.js +22 -0
- package/dist/tools/list-skills.js.map +1 -0
- package/dist/tools/load-skill.d.ts +20 -0
- package/dist/tools/load-skill.d.ts.map +1 -0
- package/dist/tools/load-skill.js +29 -0
- package/dist/tools/load-skill.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +16 -0
- package/src/skills.ts +108 -0
- package/src/tools/list-skills.ts +25 -0
- package/src/tools/load-skill.ts +32 -0
- package/tests/skills.test.ts +130 -0
- package/tests/tools/list-skills.test.ts +44 -0
- package/tests/tools/load-skill.test.ts +53 -0
package/dist/index.js
CHANGED
|
@@ -24,6 +24,8 @@ import { getSessionUsageName, getSessionUsageDescription, getSessionUsageSchema,
|
|
|
24
24
|
import { extractPostEngagementName, extractPostEngagementDescription, extractPostEngagementSchema, extractPostEngagementHandler, } from './tools/extract-post-engagement.js';
|
|
25
25
|
import { getPlaceReviewsName, getPlaceReviewsDescription, getPlaceReviewsSchema, getPlaceReviewsHandler, } from './tools/get-place-reviews.js';
|
|
26
26
|
import { listDataSourcesName, listDataSourcesDescription, listDataSourcesSchema, listDataSourcesHandler, } from './tools/list-data-sources.js';
|
|
27
|
+
import { listSkillsName, listSkillsDescription, listSkillsSchema, listSkillsHandler, } from './tools/list-skills.js';
|
|
28
|
+
import { loadSkillName, loadSkillDescription, loadSkillSchema, loadSkillHandler, } from './tools/load-skill.js';
|
|
27
29
|
// Initialize HTTP client (reads env vars)
|
|
28
30
|
initClient();
|
|
29
31
|
const server = new McpServer({
|
|
@@ -62,6 +64,8 @@ server.tool(fetchPageContentName, fetchPageContentDescription, fetchPageContentS
|
|
|
62
64
|
server.tool(getCreditBalanceName, getCreditBalanceDescription, getCreditBalanceSchema, getCreditBalanceHandler);
|
|
63
65
|
server.tool(getSessionUsageName, getSessionUsageDescription, getSessionUsageSchema, getSessionUsageHandler);
|
|
64
66
|
server.tool(listDataSourcesName, listDataSourcesDescription, listDataSourcesSchema, listDataSourcesHandler);
|
|
67
|
+
server.tool(listSkillsName, listSkillsDescription, listSkillsSchema, listSkillsHandler);
|
|
68
|
+
server.tool(loadSkillName, loadSkillDescription, loadSkillSchema, loadSkillHandler);
|
|
65
69
|
server.tool(extractPostEngagementName, extractPostEngagementDescription, extractPostEngagementSchema, extractPostEngagementHandler);
|
|
66
70
|
server.tool(getPlaceReviewsName, getPlaceReviewsDescription, getPlaceReviewsSchema, getPlaceReviewsHandler);
|
|
67
71
|
// Connect via stdio transport
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,+BAA+B,CAAA;AAEtC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,oCAAoC,CAAA;AAE3C,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,8BAA8B,CAAA;AAErC,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,uBAAuB,CAAA;AAE9B,0CAA0C;AAC1C,UAAU,EAAE,CAAA;AAEZ,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;IACE,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,OAAO;CACjB,EACD;IACE,wEAAwE;IACxE,sEAAsE;IACtE,4EAA4E;IAC5E,0EAA0E;IAC1E,YAAY,EAAE;QACZ,0FAA0F;QAC1F,qPAAqP;QACrP,0LAA0L;QAC1L,sGAAsG;KACvG,CAAC,IAAI,CAAC,IAAI,CAAC;CACb,CACF,CAAA;AAED,qBAAqB;AACrB,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAA;AAC3G,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAA;AACvF,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAA;AACnF,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAA;AAC/F,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAA;AACvF,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAA;AAC3F,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAA;AACnF,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,oBAAoB,CAAC,CAAA;AACnG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAA;AACnF,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAA;AACvF,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAA;AACnF,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAA;AAC/F,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAA;AAC3G,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAA;AAC/F,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAA;AACnF,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAA;AAC3F,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,uBAAuB,CAAC,CAAA;AAC/G,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,uBAAuB,CAAC,CAAA;AAC/G,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAA;AAC3G,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAA;AAC3G,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAA;AACvF,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAA;AACnF,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,gCAAgC,EAAE,2BAA2B,EAAE,4BAA4B,CAAC,CAAA;AACnI,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAA;AAE3G,8BAA8B;AAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;AAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA"}
|
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface SkillEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
url: string;
|
|
5
|
+
digest?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Test seam: clear in-memory caches. */
|
|
8
|
+
export declare function __resetSkillsCache(): void;
|
|
9
|
+
/** The catalog: each skill's name + one-line description (no bodies). */
|
|
10
|
+
export declare function listSkills(): Promise<{
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
}[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Full playbook body for `name`. Any `resources/<file>.md` the skill references
|
|
16
|
+
* is fetched and appended so the returned document is self-contained (agents
|
|
17
|
+
* reached over MCP can't follow the relative links themselves).
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadSkill(name: string): Promise<string>;
|
|
20
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAUD,yCAAyC;AACzC,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AA+BD,yEAAyE;AACzE,wBAAsB,UAAU,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAGnF;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B7D"}
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ColdIQ GTM skills, served over MCP.
|
|
2
|
+
//
|
|
3
|
+
// Skills are step-by-step playbooks authored in the coldiq-marketplace-skills
|
|
4
|
+
// repo. Claude Code and Cursor load them natively (progressive disclosure), but
|
|
5
|
+
// agents without a skills loader (Codex, Windsurf, Cline, …) can't. Since every
|
|
6
|
+
// agent that installs this MCP gets these two tools, the MCP is the universal
|
|
7
|
+
// delivery channel: list_skills surfaces the catalog, load_skill fetches one
|
|
8
|
+
// playbook on demand.
|
|
9
|
+
//
|
|
10
|
+
// Source is the repo's discovery index on GitHub raw, so a push to the skills
|
|
11
|
+
// repo updates what every agent sees — no MCP republish needed. Override with
|
|
12
|
+
// COLDIQ_SKILLS_INDEX_URL.
|
|
13
|
+
const DEFAULT_INDEX_URL = 'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json';
|
|
14
|
+
const INDEX_TTL_MS = 10 * 60 * 1000; // re-fetch the catalog at most every 10 min
|
|
15
|
+
let _indexCache = null;
|
|
16
|
+
const _bodyCache = new Map();
|
|
17
|
+
/** Test seam: clear in-memory caches. */
|
|
18
|
+
export function __resetSkillsCache() {
|
|
19
|
+
_indexCache = null;
|
|
20
|
+
_bodyCache.clear();
|
|
21
|
+
}
|
|
22
|
+
function indexUrl() {
|
|
23
|
+
return process.env.COLDIQ_SKILLS_INDEX_URL || DEFAULT_INDEX_URL;
|
|
24
|
+
}
|
|
25
|
+
async function fetchText(url) {
|
|
26
|
+
const res = await fetch(url, { headers: { 'user-agent': 'coldiq-mcp' } });
|
|
27
|
+
if (!res.ok)
|
|
28
|
+
throw new Error(`request to ${url} failed (HTTP ${res.status})`);
|
|
29
|
+
return await res.text();
|
|
30
|
+
}
|
|
31
|
+
async function getIndex() {
|
|
32
|
+
if (_indexCache && Date.now() - _indexCache.fetchedAt < INDEX_TTL_MS) {
|
|
33
|
+
return _indexCache.skills;
|
|
34
|
+
}
|
|
35
|
+
const parsed = JSON.parse(await fetchText(indexUrl()));
|
|
36
|
+
const raw = Array.isArray(parsed?.skills) ? parsed.skills : [];
|
|
37
|
+
const skills = raw
|
|
38
|
+
.filter((s) => !!s && typeof s === 'object')
|
|
39
|
+
.map((s) => ({
|
|
40
|
+
name: String(s.name ?? ''),
|
|
41
|
+
description: String(s.description ?? ''),
|
|
42
|
+
url: String(s.url ?? ''),
|
|
43
|
+
digest: typeof s.digest === 'string' ? s.digest : undefined,
|
|
44
|
+
}))
|
|
45
|
+
.filter((s) => s.name && s.url);
|
|
46
|
+
_indexCache = { skills, fetchedAt: Date.now() };
|
|
47
|
+
return skills;
|
|
48
|
+
}
|
|
49
|
+
/** The catalog: each skill's name + one-line description (no bodies). */
|
|
50
|
+
export async function listSkills() {
|
|
51
|
+
const skills = await getIndex();
|
|
52
|
+
return skills.map((s) => ({ name: s.name, description: s.description }));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Full playbook body for `name`. Any `resources/<file>.md` the skill references
|
|
56
|
+
* is fetched and appended so the returned document is self-contained (agents
|
|
57
|
+
* reached over MCP can't follow the relative links themselves).
|
|
58
|
+
*/
|
|
59
|
+
export async function loadSkill(name) {
|
|
60
|
+
const skills = await getIndex();
|
|
61
|
+
const entry = skills.find((s) => s.name === name);
|
|
62
|
+
if (!entry) {
|
|
63
|
+
throw new Error(`unknown skill "${name}". Available: ${skills.map((s) => s.name).join(', ')}`);
|
|
64
|
+
}
|
|
65
|
+
if (_bodyCache.has(entry.url))
|
|
66
|
+
return _bodyCache.get(entry.url);
|
|
67
|
+
const body = await fetchText(entry.url);
|
|
68
|
+
const refs = new Set();
|
|
69
|
+
for (const m of body.matchAll(/resources\/([A-Za-z0-9._-]+\.md)/g))
|
|
70
|
+
refs.add(m[1]);
|
|
71
|
+
let combined = body;
|
|
72
|
+
if (refs.size > 0) {
|
|
73
|
+
const baseDir = entry.url.replace(/\/[^/]*$/, ''); // strip trailing SKILL.md
|
|
74
|
+
const sections = [];
|
|
75
|
+
for (const ref of refs) {
|
|
76
|
+
try {
|
|
77
|
+
const text = await fetchText(`${baseDir}/resources/${ref}`);
|
|
78
|
+
sections.push(`\n\n---\n\n## Resource: resources/${ref}\n\n${text.trim()}`);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// A missing resource shouldn't fail the whole skill load.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
combined = body.replace(/\s+$/, '') + sections.join('') + '\n';
|
|
85
|
+
}
|
|
86
|
+
_bodyCache.set(entry.url, combined);
|
|
87
|
+
return combined;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const listSkillsName = "list_skills";
|
|
2
|
+
export declare const listSkillsDescription: string;
|
|
3
|
+
export declare const listSkillsSchema: {};
|
|
4
|
+
export declare function listSkillsHandler(_input: Record<string, unknown>): Promise<{
|
|
5
|
+
content: {
|
|
6
|
+
type: "text";
|
|
7
|
+
text: string;
|
|
8
|
+
}[];
|
|
9
|
+
isError?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
isError: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
//# sourceMappingURL=list-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-skills.d.ts","sourceRoot":"","sources":["../../src/tools/list-skills.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,gBAAgB,CAAA;AAE3C,eAAO,MAAM,qBAAqB,QAMkE,CAAA;AAEpG,eAAO,MAAM,gBAAgB,IAAK,CAAA;AAElC,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAUtE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { listSkills } from '../skills.js';
|
|
2
|
+
export const listSkillsName = 'list_skills';
|
|
3
|
+
export const listSkillsDescription = 'List the available ColdIQ GTM playbooks ("skills") — step-by-step expert workflows for go-to-market ' +
|
|
4
|
+
'tasks: building a TAM, Apollo / company / people search, contact enrichment and email waterfalls, ' +
|
|
5
|
+
'signal detection, ICP & personas, cold-email copy, campaign delivery, and more. Returns each skill\'s ' +
|
|
6
|
+
'name and a one-line description of when to use it. Call this when starting a prospecting / outreach / ' +
|
|
7
|
+
'GTM task to check for a relevant playbook, then call load_skill to read the full step-by-step guide. ' +
|
|
8
|
+
'(Claude Code and Cursor load these skills natively; on other agents this is how you reach them.)';
|
|
9
|
+
export const listSkillsSchema = {};
|
|
10
|
+
export async function listSkillsHandler(_input) {
|
|
11
|
+
try {
|
|
12
|
+
const skills = await listSkills();
|
|
13
|
+
return { content: [{ type: 'text', text: JSON.stringify({ data: skills }) }] };
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: JSON.stringify({ error: `Could not load the ColdIQ skills catalog: ${err.message}` }) }],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=list-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-skills.js","sourceRoot":"","sources":["../../src/tools/list-skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAA;AAE3C,MAAM,CAAC,MAAM,qBAAqB,GAChC,sGAAsG;IACtG,oGAAoG;IACpG,wGAAwG;IACxG,wGAAwG;IACxG,uGAAuG;IACvG,kGAAkG,CAAA;AAEpG,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAElC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAA+B;IACrE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAA;QACjC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAA;IACzF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,6CAA8C,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;YAC5I,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const loadSkillName = "load_skill";
|
|
3
|
+
export declare const loadSkillDescription: string;
|
|
4
|
+
export declare const loadSkillSchema: {
|
|
5
|
+
name: z.ZodString;
|
|
6
|
+
};
|
|
7
|
+
export declare function loadSkillHandler(input: Record<string, unknown>): Promise<{
|
|
8
|
+
content: {
|
|
9
|
+
type: "text";
|
|
10
|
+
text: string;
|
|
11
|
+
}[];
|
|
12
|
+
isError: boolean;
|
|
13
|
+
} | {
|
|
14
|
+
content: {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
isError?: undefined;
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=load-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-skill.d.ts","sourceRoot":"","sources":["../../src/tools/load-skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,aAAa,eAAe,CAAA;AAEzC,eAAO,MAAM,oBAAoB,QAIK,CAAA;AAEtC,eAAO,MAAM,eAAe;;CAI3B,CAAA;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;GAcpE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { loadSkill } from '../skills.js';
|
|
3
|
+
export const loadSkillName = 'load_skill';
|
|
4
|
+
export const loadSkillDescription = 'Load the full ColdIQ GTM playbook ("skill") by name — the complete step-by-step expert workflow, ' +
|
|
5
|
+
'including which ColdIQ tools/endpoints to call at each step. Call list_skills first to get the available ' +
|
|
6
|
+
'names. The returned guide (with any referenced reference docs inlined) tells you exactly how to run the ' +
|
|
7
|
+
'task using the other ColdIQ tools.';
|
|
8
|
+
export const loadSkillSchema = {
|
|
9
|
+
name: z
|
|
10
|
+
.string()
|
|
11
|
+
.describe('The skill name from list_skills, e.g. "contact-enrichment", "apollo-search", or "tam-scoring".'),
|
|
12
|
+
};
|
|
13
|
+
export async function loadSkillHandler(input) {
|
|
14
|
+
const name = String(input.name ?? '').trim();
|
|
15
|
+
if (!name) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: 'Provide a skill "name" — call list_skills to see the available skills.' }],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const body = await loadSkill(name);
|
|
23
|
+
return { content: [{ type: 'text', text: body }] };
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return { content: [{ type: 'text', text: err.message }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=load-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-skill.js","sourceRoot":"","sources":["../../src/tools/load-skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,MAAM,CAAC,MAAM,aAAa,GAAG,YAAY,CAAA;AAEzC,MAAM,CAAC,MAAM,oBAAoB,GAC/B,mGAAmG;IACnG,2GAA2G;IAC3G,0GAA0G;IAC1G,oCAAoC,CAAA;AAEtC,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CAAC,gGAAgG,CAAC;CAC9G,CAAA;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAA8B;IACnE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,wEAAwE,EAAE,CAAC;YACpH,OAAO,EAAE,IAAI;SACd,CAAA;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,CAAA;QAClC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAC9F,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -158,6 +158,20 @@ import {
|
|
|
158
158
|
listDataSourcesHandler,
|
|
159
159
|
} from './tools/list-data-sources.js'
|
|
160
160
|
|
|
161
|
+
import {
|
|
162
|
+
listSkillsName,
|
|
163
|
+
listSkillsDescription,
|
|
164
|
+
listSkillsSchema,
|
|
165
|
+
listSkillsHandler,
|
|
166
|
+
} from './tools/list-skills.js'
|
|
167
|
+
|
|
168
|
+
import {
|
|
169
|
+
loadSkillName,
|
|
170
|
+
loadSkillDescription,
|
|
171
|
+
loadSkillSchema,
|
|
172
|
+
loadSkillHandler,
|
|
173
|
+
} from './tools/load-skill.js'
|
|
174
|
+
|
|
161
175
|
// Initialize HTTP client (reads env vars)
|
|
162
176
|
initClient()
|
|
163
177
|
|
|
@@ -201,6 +215,8 @@ server.tool(fetchPageContentName, fetchPageContentDescription, fetchPageContentS
|
|
|
201
215
|
server.tool(getCreditBalanceName, getCreditBalanceDescription, getCreditBalanceSchema, getCreditBalanceHandler)
|
|
202
216
|
server.tool(getSessionUsageName, getSessionUsageDescription, getSessionUsageSchema, getSessionUsageHandler)
|
|
203
217
|
server.tool(listDataSourcesName, listDataSourcesDescription, listDataSourcesSchema, listDataSourcesHandler)
|
|
218
|
+
server.tool(listSkillsName, listSkillsDescription, listSkillsSchema, listSkillsHandler)
|
|
219
|
+
server.tool(loadSkillName, loadSkillDescription, loadSkillSchema, loadSkillHandler)
|
|
204
220
|
server.tool(extractPostEngagementName, extractPostEngagementDescription, extractPostEngagementSchema, extractPostEngagementHandler)
|
|
205
221
|
server.tool(getPlaceReviewsName, getPlaceReviewsDescription, getPlaceReviewsSchema, getPlaceReviewsHandler)
|
|
206
222
|
|
package/src/skills.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// ColdIQ GTM skills, served over MCP.
|
|
2
|
+
//
|
|
3
|
+
// Skills are step-by-step playbooks authored in the coldiq-marketplace-skills
|
|
4
|
+
// repo. Claude Code and Cursor load them natively (progressive disclosure), but
|
|
5
|
+
// agents without a skills loader (Codex, Windsurf, Cline, …) can't. Since every
|
|
6
|
+
// agent that installs this MCP gets these two tools, the MCP is the universal
|
|
7
|
+
// delivery channel: list_skills surfaces the catalog, load_skill fetches one
|
|
8
|
+
// playbook on demand.
|
|
9
|
+
//
|
|
10
|
+
// Source is the repo's discovery index on GitHub raw, so a push to the skills
|
|
11
|
+
// repo updates what every agent sees — no MCP republish needed. Override with
|
|
12
|
+
// COLDIQ_SKILLS_INDEX_URL.
|
|
13
|
+
|
|
14
|
+
const DEFAULT_INDEX_URL =
|
|
15
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json'
|
|
16
|
+
const INDEX_TTL_MS = 10 * 60 * 1000 // re-fetch the catalog at most every 10 min
|
|
17
|
+
|
|
18
|
+
export interface SkillEntry {
|
|
19
|
+
name: string
|
|
20
|
+
description: string
|
|
21
|
+
url: string
|
|
22
|
+
digest?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CachedIndex {
|
|
26
|
+
skills: SkillEntry[]
|
|
27
|
+
fetchedAt: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let _indexCache: CachedIndex | null = null
|
|
31
|
+
const _bodyCache = new Map<string, string>()
|
|
32
|
+
|
|
33
|
+
/** Test seam: clear in-memory caches. */
|
|
34
|
+
export function __resetSkillsCache(): void {
|
|
35
|
+
_indexCache = null
|
|
36
|
+
_bodyCache.clear()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function indexUrl(): string {
|
|
40
|
+
return process.env.COLDIQ_SKILLS_INDEX_URL || DEFAULT_INDEX_URL
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function fetchText(url: string): Promise<string> {
|
|
44
|
+
const res = await fetch(url, { headers: { 'user-agent': 'coldiq-mcp' } })
|
|
45
|
+
if (!res.ok) throw new Error(`request to ${url} failed (HTTP ${res.status})`)
|
|
46
|
+
return await res.text()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function getIndex(): Promise<SkillEntry[]> {
|
|
50
|
+
if (_indexCache && Date.now() - _indexCache.fetchedAt < INDEX_TTL_MS) {
|
|
51
|
+
return _indexCache.skills
|
|
52
|
+
}
|
|
53
|
+
const parsed = JSON.parse(await fetchText(indexUrl()))
|
|
54
|
+
const raw = Array.isArray(parsed?.skills) ? parsed.skills : []
|
|
55
|
+
const skills: SkillEntry[] = raw
|
|
56
|
+
.filter((s: unknown): s is Record<string, unknown> => !!s && typeof s === 'object')
|
|
57
|
+
.map((s: Record<string, unknown>) => ({
|
|
58
|
+
name: String(s.name ?? ''),
|
|
59
|
+
description: String(s.description ?? ''),
|
|
60
|
+
url: String(s.url ?? ''),
|
|
61
|
+
digest: typeof s.digest === 'string' ? s.digest : undefined,
|
|
62
|
+
}))
|
|
63
|
+
.filter((s: SkillEntry) => s.name && s.url)
|
|
64
|
+
_indexCache = { skills, fetchedAt: Date.now() }
|
|
65
|
+
return skills
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The catalog: each skill's name + one-line description (no bodies). */
|
|
69
|
+
export async function listSkills(): Promise<{ name: string; description: string }[]> {
|
|
70
|
+
const skills = await getIndex()
|
|
71
|
+
return skills.map((s) => ({ name: s.name, description: s.description }))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Full playbook body for `name`. Any `resources/<file>.md` the skill references
|
|
76
|
+
* is fetched and appended so the returned document is self-contained (agents
|
|
77
|
+
* reached over MCP can't follow the relative links themselves).
|
|
78
|
+
*/
|
|
79
|
+
export async function loadSkill(name: string): Promise<string> {
|
|
80
|
+
const skills = await getIndex()
|
|
81
|
+
const entry = skills.find((s) => s.name === name)
|
|
82
|
+
if (!entry) {
|
|
83
|
+
throw new Error(`unknown skill "${name}". Available: ${skills.map((s) => s.name).join(', ')}`)
|
|
84
|
+
}
|
|
85
|
+
if (_bodyCache.has(entry.url)) return _bodyCache.get(entry.url) as string
|
|
86
|
+
|
|
87
|
+
const body = await fetchText(entry.url)
|
|
88
|
+
const refs = new Set<string>()
|
|
89
|
+
for (const m of body.matchAll(/resources\/([A-Za-z0-9._-]+\.md)/g)) refs.add(m[1])
|
|
90
|
+
|
|
91
|
+
let combined = body
|
|
92
|
+
if (refs.size > 0) {
|
|
93
|
+
const baseDir = entry.url.replace(/\/[^/]*$/, '') // strip trailing SKILL.md
|
|
94
|
+
const sections: string[] = []
|
|
95
|
+
for (const ref of refs) {
|
|
96
|
+
try {
|
|
97
|
+
const text = await fetchText(`${baseDir}/resources/${ref}`)
|
|
98
|
+
sections.push(`\n\n---\n\n## Resource: resources/${ref}\n\n${text.trim()}`)
|
|
99
|
+
} catch {
|
|
100
|
+
// A missing resource shouldn't fail the whole skill load.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
combined = body.replace(/\s+$/, '') + sections.join('') + '\n'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_bodyCache.set(entry.url, combined)
|
|
107
|
+
return combined
|
|
108
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { listSkills } from '../skills.js'
|
|
2
|
+
|
|
3
|
+
export const listSkillsName = 'list_skills'
|
|
4
|
+
|
|
5
|
+
export const listSkillsDescription =
|
|
6
|
+
'List the available ColdIQ GTM playbooks ("skills") — step-by-step expert workflows for go-to-market ' +
|
|
7
|
+
'tasks: building a TAM, Apollo / company / people search, contact enrichment and email waterfalls, ' +
|
|
8
|
+
'signal detection, ICP & personas, cold-email copy, campaign delivery, and more. Returns each skill\'s ' +
|
|
9
|
+
'name and a one-line description of when to use it. Call this when starting a prospecting / outreach / ' +
|
|
10
|
+
'GTM task to check for a relevant playbook, then call load_skill to read the full step-by-step guide. ' +
|
|
11
|
+
'(Claude Code and Cursor load these skills natively; on other agents this is how you reach them.)'
|
|
12
|
+
|
|
13
|
+
export const listSkillsSchema = {}
|
|
14
|
+
|
|
15
|
+
export async function listSkillsHandler(_input: Record<string, unknown>) {
|
|
16
|
+
try {
|
|
17
|
+
const skills = await listSkills()
|
|
18
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify({ data: skills }) }] }
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text' as const, text: JSON.stringify({ error: `Could not load the ColdIQ skills catalog: ${(err as Error).message}` }) }],
|
|
22
|
+
isError: true,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { loadSkill } from '../skills.js'
|
|
3
|
+
|
|
4
|
+
export const loadSkillName = 'load_skill'
|
|
5
|
+
|
|
6
|
+
export const loadSkillDescription =
|
|
7
|
+
'Load the full ColdIQ GTM playbook ("skill") by name — the complete step-by-step expert workflow, ' +
|
|
8
|
+
'including which ColdIQ tools/endpoints to call at each step. Call list_skills first to get the available ' +
|
|
9
|
+
'names. The returned guide (with any referenced reference docs inlined) tells you exactly how to run the ' +
|
|
10
|
+
'task using the other ColdIQ tools.'
|
|
11
|
+
|
|
12
|
+
export const loadSkillSchema = {
|
|
13
|
+
name: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe('The skill name from list_skills, e.g. "contact-enrichment", "apollo-search", or "tam-scoring".'),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function loadSkillHandler(input: Record<string, unknown>) {
|
|
19
|
+
const name = String(input.name ?? '').trim()
|
|
20
|
+
if (!name) {
|
|
21
|
+
return {
|
|
22
|
+
content: [{ type: 'text' as const, text: 'Provide a skill "name" — call list_skills to see the available skills.' }],
|
|
23
|
+
isError: true,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const body = await loadSkill(name)
|
|
28
|
+
return { content: [{ type: 'text' as const, text: body }] }
|
|
29
|
+
} catch (err) {
|
|
30
|
+
return { content: [{ type: 'text' as const, text: (err as Error).message }], isError: true }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
+
import { listSkills, loadSkill, __resetSkillsCache } from '../src/skills.js'
|
|
3
|
+
|
|
4
|
+
const INDEX = {
|
|
5
|
+
$schema: 'https://schemas.agentskills.io/discovery/0.2.0/schema.json',
|
|
6
|
+
name: 'coldiq',
|
|
7
|
+
skills: [
|
|
8
|
+
{
|
|
9
|
+
name: 'contact-enrichment',
|
|
10
|
+
type: 'skill-md',
|
|
11
|
+
description: 'Enrich contacts with emails and phones.',
|
|
12
|
+
url: 'https://raw.example/skills/contact-enrichment/SKILL.md',
|
|
13
|
+
digest: 'sha256:abc',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'coldiq-search-enrich',
|
|
17
|
+
type: 'skill-md',
|
|
18
|
+
description: 'Search and enrich at scale.',
|
|
19
|
+
url: 'https://raw.example/skills/lima-data-api/SKILL.md',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const BODY_WITH_RES =
|
|
25
|
+
'# Contact enrichment\n\nSee [resources/async-job-pattern.md](resources/async-job-pattern.md) and ' +
|
|
26
|
+
'[resources/credit-optimization.md](resources/credit-optimization.md).\n'
|
|
27
|
+
|
|
28
|
+
function mockFetch(map: Record<string, { status?: number; body: string }>) {
|
|
29
|
+
return vi.fn(async (url: string | URL | Request) => {
|
|
30
|
+
const u = String(url)
|
|
31
|
+
const hit = map[u]
|
|
32
|
+
if (!hit) return { ok: false, status: 404, text: async () => 'not found' } as Response
|
|
33
|
+
const status = hit.status ?? 200
|
|
34
|
+
return { ok: status >= 200 && status < 300, status, text: async () => hit.body } as Response
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const originalFetch = globalThis.fetch
|
|
39
|
+
|
|
40
|
+
describe('skills module', () => {
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
__resetSkillsCache()
|
|
43
|
+
delete process.env.COLDIQ_SKILLS_INDEX_URL
|
|
44
|
+
})
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
globalThis.fetch = originalFetch
|
|
47
|
+
vi.restoreAllMocks()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('listSkills returns name + description from the index', async () => {
|
|
51
|
+
globalThis.fetch = mockFetch({
|
|
52
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json': {
|
|
53
|
+
body: JSON.stringify(INDEX),
|
|
54
|
+
},
|
|
55
|
+
}) as typeof fetch
|
|
56
|
+
const skills = await listSkills()
|
|
57
|
+
expect(skills).toHaveLength(2)
|
|
58
|
+
expect(skills[0]).toEqual({ name: 'contact-enrichment', description: 'Enrich contacts with emails and phones.' })
|
|
59
|
+
expect(skills.map((s) => s.name)).toContain('coldiq-search-enrich')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('honors COLDIQ_SKILLS_INDEX_URL override', async () => {
|
|
63
|
+
process.env.COLDIQ_SKILLS_INDEX_URL = 'https://custom/index.json'
|
|
64
|
+
globalThis.fetch = mockFetch({ 'https://custom/index.json': { body: JSON.stringify(INDEX) } }) as typeof fetch
|
|
65
|
+
const skills = await listSkills()
|
|
66
|
+
expect(skills).toHaveLength(2)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('caches the index (one network call across two listSkills)', async () => {
|
|
70
|
+
const f = mockFetch({
|
|
71
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json': {
|
|
72
|
+
body: JSON.stringify(INDEX),
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
globalThis.fetch = f as typeof fetch
|
|
76
|
+
await listSkills()
|
|
77
|
+
await listSkills()
|
|
78
|
+
expect(f).toHaveBeenCalledTimes(1)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('loadSkill returns the body with referenced resources inlined', async () => {
|
|
82
|
+
globalThis.fetch = mockFetch({
|
|
83
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json': {
|
|
84
|
+
body: JSON.stringify(INDEX),
|
|
85
|
+
},
|
|
86
|
+
'https://raw.example/skills/contact-enrichment/SKILL.md': { body: BODY_WITH_RES },
|
|
87
|
+
'https://raw.example/skills/contact-enrichment/resources/async-job-pattern.md': { body: 'POLL LOOP DOC' },
|
|
88
|
+
'https://raw.example/skills/contact-enrichment/resources/credit-optimization.md': { body: 'CREDIT DOC' },
|
|
89
|
+
}) as typeof fetch
|
|
90
|
+
const doc = await loadSkill('contact-enrichment')
|
|
91
|
+
expect(doc).toContain('# Contact enrichment')
|
|
92
|
+
expect(doc).toContain('## Resource: resources/async-job-pattern.md')
|
|
93
|
+
expect(doc).toContain('POLL LOOP DOC')
|
|
94
|
+
expect(doc).toContain('## Resource: resources/credit-optimization.md')
|
|
95
|
+
expect(doc).toContain('CREDIT DOC')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('loadSkill tolerates a missing resource (skips it, still returns body)', async () => {
|
|
99
|
+
globalThis.fetch = mockFetch({
|
|
100
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json': {
|
|
101
|
+
body: JSON.stringify(INDEX),
|
|
102
|
+
},
|
|
103
|
+
'https://raw.example/skills/contact-enrichment/SKILL.md': { body: BODY_WITH_RES },
|
|
104
|
+
// resources intentionally 404
|
|
105
|
+
}) as typeof fetch
|
|
106
|
+
const doc = await loadSkill('contact-enrichment')
|
|
107
|
+
expect(doc).toContain('# Contact enrichment')
|
|
108
|
+
expect(doc).not.toContain('POLL LOOP DOC')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('loadSkill resolves folder≠name via the index url', async () => {
|
|
112
|
+
globalThis.fetch = mockFetch({
|
|
113
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json': {
|
|
114
|
+
body: JSON.stringify(INDEX),
|
|
115
|
+
},
|
|
116
|
+
'https://raw.example/skills/lima-data-api/SKILL.md': { body: '# Search & enrich\n' },
|
|
117
|
+
}) as typeof fetch
|
|
118
|
+
const doc = await loadSkill('coldiq-search-enrich')
|
|
119
|
+
expect(doc).toContain('# Search & enrich')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('loadSkill throws a helpful error for an unknown skill', async () => {
|
|
123
|
+
globalThis.fetch = mockFetch({
|
|
124
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json': {
|
|
125
|
+
body: JSON.stringify(INDEX),
|
|
126
|
+
},
|
|
127
|
+
}) as typeof fetch
|
|
128
|
+
await expect(loadSkill('does-not-exist')).rejects.toThrow(/unknown skill/i)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
listSkillsHandler,
|
|
4
|
+
listSkillsName,
|
|
5
|
+
listSkillsSchema,
|
|
6
|
+
} from '../../src/tools/list-skills.js'
|
|
7
|
+
import { __resetSkillsCache } from '../../src/skills.js'
|
|
8
|
+
|
|
9
|
+
const INDEX_URL =
|
|
10
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json'
|
|
11
|
+
const originalFetch = globalThis.fetch
|
|
12
|
+
|
|
13
|
+
describe('list_skills handler', () => {
|
|
14
|
+
beforeEach(() => __resetSkillsCache())
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
globalThis.fetch = originalFetch
|
|
17
|
+
vi.restoreAllMocks()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('is a zero-arg tool', () => {
|
|
21
|
+
expect(listSkillsName).toBe('list_skills')
|
|
22
|
+
expect(listSkillsSchema).toEqual({})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('returns the catalog as { data: [...] }', async () => {
|
|
26
|
+
globalThis.fetch = vi.fn(async () => ({
|
|
27
|
+
ok: true,
|
|
28
|
+
status: 200,
|
|
29
|
+
text: async () =>
|
|
30
|
+
JSON.stringify({ skills: [{ name: 'tam-scoring', description: 'Score a TAM.', url: 'https://x/SKILL.md' }] }),
|
|
31
|
+
})) as unknown as typeof fetch
|
|
32
|
+
const res = await listSkillsHandler({})
|
|
33
|
+
expect(res.isError).toBeFalsy()
|
|
34
|
+
const parsed = JSON.parse(res.content[0].text)
|
|
35
|
+
expect(parsed.data).toEqual([{ name: 'tam-scoring', description: 'Score a TAM.' }])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('returns an error result (not a throw) when the index is unreachable', async () => {
|
|
39
|
+
globalThis.fetch = vi.fn(async () => ({ ok: false, status: 500, text: async () => 'err' })) as unknown as typeof fetch
|
|
40
|
+
const res = await listSkillsHandler({})
|
|
41
|
+
expect(res.isError).toBe(true)
|
|
42
|
+
expect(JSON.parse(res.content[0].text).error).toMatch(/could not load/i)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
2
|
+
import { loadSkillHandler, loadSkillName, loadSkillSchema } from '../../src/tools/load-skill.js'
|
|
3
|
+
import { __resetSkillsCache } from '../../src/skills.js'
|
|
4
|
+
|
|
5
|
+
const INDEX_URL =
|
|
6
|
+
'https://raw.githubusercontent.com/Cold-IQ/coldiq-marketplace-skills/main/.well-known/agent-skills/index.json'
|
|
7
|
+
|
|
8
|
+
function mockFetch(map: Record<string, string>) {
|
|
9
|
+
return vi.fn(async (url: string | URL | Request) => {
|
|
10
|
+
const body = map[String(url)]
|
|
11
|
+
return { ok: body !== undefined, status: body !== undefined ? 200 : 404, text: async () => body ?? 'nf' } as Response
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
const originalFetch = globalThis.fetch
|
|
15
|
+
|
|
16
|
+
describe('load_skill handler', () => {
|
|
17
|
+
beforeEach(() => __resetSkillsCache())
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
globalThis.fetch = originalFetch
|
|
20
|
+
vi.restoreAllMocks()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('declares a required name arg', () => {
|
|
24
|
+
expect(loadSkillName).toBe('load_skill')
|
|
25
|
+
expect(Object.keys(loadSkillSchema)).toContain('name')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('returns the skill body as text', async () => {
|
|
29
|
+
globalThis.fetch = mockFetch({
|
|
30
|
+
[INDEX_URL]: JSON.stringify({ skills: [{ name: 'apollo-search', description: 'd', url: 'https://x/skills/apollo-search/SKILL.md' }] }),
|
|
31
|
+
'https://x/skills/apollo-search/SKILL.md': '# Apollo search\nstep 1',
|
|
32
|
+
}) as typeof fetch
|
|
33
|
+
const res = await loadSkillHandler({ name: 'apollo-search' })
|
|
34
|
+
expect(res.isError).toBeFalsy()
|
|
35
|
+
expect(res.content[0].text).toContain('# Apollo search')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('errors (not throws) on a missing name', async () => {
|
|
39
|
+
const res = await loadSkillHandler({})
|
|
40
|
+
expect(res.isError).toBe(true)
|
|
41
|
+
expect(res.content[0].text).toMatch(/list_skills/)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('errors with the available list on an unknown skill', async () => {
|
|
45
|
+
globalThis.fetch = mockFetch({
|
|
46
|
+
[INDEX_URL]: JSON.stringify({ skills: [{ name: 'apollo-search', description: 'd', url: 'https://x/SKILL.md' }] }),
|
|
47
|
+
}) as typeof fetch
|
|
48
|
+
const res = await loadSkillHandler({ name: 'nope' })
|
|
49
|
+
expect(res.isError).toBe(true)
|
|
50
|
+
expect(res.content[0].text).toMatch(/unknown skill/i)
|
|
51
|
+
expect(res.content[0].text).toMatch(/apollo-search/)
|
|
52
|
+
})
|
|
53
|
+
})
|