@frase/mcp-server 0.1.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.
- package/CHANGELOG.md +44 -0
- package/README.md +262 -0
- package/dist/api-client.d.ts +93 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +213 -0
- package/dist/api-client.js.map +1 -0
- package/dist/cache.d.ts +52 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +97 -0
- package/dist/cache.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/formatter.d.ts +47 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +136 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +292 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/competitor-analysis.d.ts +18 -0
- package/dist/prompts/competitor-analysis.d.ts.map +1 -0
- package/dist/prompts/competitor-analysis.js +71 -0
- package/dist/prompts/competitor-analysis.js.map +1 -0
- package/dist/prompts/content-audit.d.ts +18 -0
- package/dist/prompts/content-audit.d.ts.map +1 -0
- package/dist/prompts/content-audit.js +67 -0
- package/dist/prompts/content-audit.js.map +1 -0
- package/dist/prompts/create-seo-article.d.ts +19 -0
- package/dist/prompts/create-seo-article.d.ts.map +1 -0
- package/dist/prompts/create-seo-article.js +78 -0
- package/dist/prompts/create-seo-article.js.map +1 -0
- package/dist/prompts/index.d.ts +18 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +52 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/keyword-research.d.ts +18 -0
- package/dist/prompts/keyword-research.d.ts.map +1 -0
- package/dist/prompts/keyword-research.js +72 -0
- package/dist/prompts/keyword-research.js.map +1 -0
- package/dist/prompts/optimize-content.d.ts +18 -0
- package/dist/prompts/optimize-content.d.ts.map +1 -0
- package/dist/prompts/optimize-content.js +59 -0
- package/dist/prompts/optimize-content.js.map +1 -0
- package/dist/resources/briefs.d.ts +26 -0
- package/dist/resources/briefs.d.ts.map +1 -0
- package/dist/resources/briefs.js +144 -0
- package/dist/resources/briefs.js.map +1 -0
- package/dist/resources/content.d.ts +26 -0
- package/dist/resources/content.d.ts.map +1 -0
- package/dist/resources/content.js +128 -0
- package/dist/resources/content.js.map +1 -0
- package/dist/resources/index.d.ts +32 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +85 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/sites.d.ts +26 -0
- package/dist/resources/sites.d.ts.map +1 -0
- package/dist/resources/sites.js +108 -0
- package/dist/resources/sites.js.map +1 -0
- package/dist/tools/ai-visibility.d.ts +25 -0
- package/dist/tools/ai-visibility.d.ts.map +1 -0
- package/dist/tools/ai-visibility.js +537 -0
- package/dist/tools/ai-visibility.js.map +1 -0
- package/dist/tools/analytics.d.ts +17 -0
- package/dist/tools/analytics.d.ts.map +1 -0
- package/dist/tools/analytics.js +311 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/audits.d.ts +73 -0
- package/dist/tools/audits.d.ts.map +1 -0
- package/dist/tools/audits.js +345 -0
- package/dist/tools/audits.js.map +1 -0
- package/dist/tools/briefs.d.ts +63 -0
- package/dist/tools/briefs.d.ts.map +1 -0
- package/dist/tools/briefs.js +276 -0
- package/dist/tools/briefs.js.map +1 -0
- package/dist/tools/content.d.ts +51 -0
- package/dist/tools/content.d.ts.map +1 -0
- package/dist/tools/content.js +233 -0
- package/dist/tools/content.js.map +1 -0
- package/dist/tools/index.d.ts +29 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +96 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/jobs.d.ts +22 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +124 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/optimizations.d.ts +19 -0
- package/dist/tools/optimizations.d.ts.map +1 -0
- package/dist/tools/optimizations.js +339 -0
- package/dist/tools/optimizations.js.map +1 -0
- package/dist/tools/research.d.ts +41 -0
- package/dist/tools/research.d.ts.map +1 -0
- package/dist/tools/research.js +151 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/tools/serp.d.ts +15 -0
- package/dist/tools/serp.d.ts.map +1 -0
- package/dist/tools/serp.js +267 -0
- package/dist/tools/serp.js.map +1 -0
- package/dist/tools/sites.d.ts +31 -0
- package/dist/tools/sites.d.ts.map +1 -0
- package/dist/tools/sites.js +83 -0
- package/dist/tools/sites.js.map +1 -0
- package/dist/tools/webhooks.d.ts +19 -0
- package/dist/tools/webhooks.d.ts.map +1 -0
- package/dist/tools/webhooks.js +350 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/dist/types.d.ts +167 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
package/dist/cache.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple in-memory cache for GET responses
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Cache TTL configuration (in milliseconds)
|
|
6
|
+
*/
|
|
7
|
+
export const CACHE_TTL = {
|
|
8
|
+
lists: 60_000, // 1 minute for list endpoints
|
|
9
|
+
resources: 300_000, // 5 minutes for individual resources
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Simple cache implementation
|
|
13
|
+
*/
|
|
14
|
+
export class ResponseCache {
|
|
15
|
+
cache = new Map();
|
|
16
|
+
cleanupInterval = null;
|
|
17
|
+
constructor() {
|
|
18
|
+
// Run cleanup every minute
|
|
19
|
+
this.cleanupInterval = setInterval(() => this.cleanup(), 60_000);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get a cached value
|
|
23
|
+
*/
|
|
24
|
+
get(key) {
|
|
25
|
+
const entry = this.cache.get(key);
|
|
26
|
+
if (!entry) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
if (Date.now() > entry.expiresAt) {
|
|
30
|
+
this.cache.delete(key);
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return entry.data;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set a cached value
|
|
37
|
+
*/
|
|
38
|
+
set(key, data, ttl) {
|
|
39
|
+
this.cache.set(key, {
|
|
40
|
+
data,
|
|
41
|
+
expiresAt: Date.now() + ttl,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Generate cache key for a request
|
|
46
|
+
*/
|
|
47
|
+
static key(method, path, params) {
|
|
48
|
+
const paramStr = params ? JSON.stringify(params) : "";
|
|
49
|
+
return `${method}:${path}:${paramStr}`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Invalidate cache entries matching a pattern
|
|
53
|
+
*/
|
|
54
|
+
invalidate(pattern) {
|
|
55
|
+
const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
|
|
56
|
+
for (const key of this.cache.keys()) {
|
|
57
|
+
if (regex.test(key)) {
|
|
58
|
+
this.cache.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Invalidate all cache entries for a resource type
|
|
64
|
+
* Called after write operations
|
|
65
|
+
*/
|
|
66
|
+
invalidateResourceType(resourceType) {
|
|
67
|
+
this.invalidate(new RegExp(`:/${resourceType}`));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Clear all cached entries
|
|
71
|
+
*/
|
|
72
|
+
clear() {
|
|
73
|
+
this.cache.clear();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Clean up expired entries
|
|
77
|
+
*/
|
|
78
|
+
cleanup() {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
81
|
+
if (now > entry.expiresAt) {
|
|
82
|
+
this.cache.delete(key);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Stop the cleanup interval (for graceful shutdown)
|
|
88
|
+
*/
|
|
89
|
+
destroy() {
|
|
90
|
+
if (this.cleanupInterval) {
|
|
91
|
+
clearInterval(this.cleanupInterval);
|
|
92
|
+
this.cleanupInterval = null;
|
|
93
|
+
}
|
|
94
|
+
this.cache.clear();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the Frase MCP Server
|
|
3
|
+
*/
|
|
4
|
+
export interface Config {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
debug: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Load configuration from environment variables
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadConfig(): Config;
|
|
13
|
+
/**
|
|
14
|
+
* Log debug message if debug mode is enabled
|
|
15
|
+
*/
|
|
16
|
+
export declare function debugLog(config: Config, ...args: unknown[]): void;
|
|
17
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAOD;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAenC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAIjE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the Frase MCP Server
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Default API URL
|
|
6
|
+
*/
|
|
7
|
+
const DEFAULT_API_URL = "https://next.frase.io/api/v1";
|
|
8
|
+
/**
|
|
9
|
+
* Load configuration from environment variables
|
|
10
|
+
*/
|
|
11
|
+
export function loadConfig() {
|
|
12
|
+
const apiKey = process.env.FRASE_API_KEY;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new Error("FRASE_API_KEY environment variable is required. " +
|
|
15
|
+
"Get your API key from https://app.frase.io/settings/api");
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
apiKey,
|
|
19
|
+
apiUrl: process.env.FRASE_API_URL || DEFAULT_API_URL,
|
|
20
|
+
debug: process.env.FRASE_MCP_DEBUG === "true",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Log debug message if debug mode is enabled
|
|
25
|
+
*/
|
|
26
|
+
export function debugLog(config, ...args) {
|
|
27
|
+
if (config.debug) {
|
|
28
|
+
console.error("[Frase MCP Debug]", ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH;;GAEG;AACH,MAAM,eAAe,GAAG,8BAA8B,CAAC;AAEvD;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAEzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,kDAAkD;YAChD,yDAAyD,CAC5D,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM;QACN,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,eAAe;QACpD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,MAAM;KAC9C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,GAAG,IAAe;IACzD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown response formatter for MCP tools
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format a date for display
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatDate(date: string | Date): string;
|
|
8
|
+
/**
|
|
9
|
+
* Format status with emoji
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatStatus(status: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Format a list of items as a markdown table
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatTable(headers: string[], rows: string[][], options?: {
|
|
16
|
+
align?: ("left" | "center" | "right")[];
|
|
17
|
+
}): string;
|
|
18
|
+
/**
|
|
19
|
+
* Format pagination info
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatPagination(pagination: {
|
|
22
|
+
page: number;
|
|
23
|
+
page_size: number;
|
|
24
|
+
total: number;
|
|
25
|
+
has_more: boolean;
|
|
26
|
+
}): string;
|
|
27
|
+
/**
|
|
28
|
+
* Format a success response
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatSuccess(title: string, content: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Format an error response
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatError(error: Error | string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Format a key-value list
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatKeyValue(items: Record<string, string | number | boolean | null | undefined>): string;
|
|
39
|
+
/**
|
|
40
|
+
* Format word count
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatWordCount(count: number | null | undefined): string;
|
|
43
|
+
/**
|
|
44
|
+
* Truncate text with ellipsis
|
|
45
|
+
*/
|
|
46
|
+
export declare function truncate(text: string, maxLength: number): string;
|
|
47
|
+
//# sourceMappingURL=formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAOtD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA2BnD;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,MAAM,EAAE,EAAE,EAChB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAA;CAAE,GACpD,MAAM,CAiCR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACnB,GAAG,MAAM,CAYT;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,MAAM,CAa1G;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAUxE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMhE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown response formatter for MCP tools
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format a date for display
|
|
6
|
+
*/
|
|
7
|
+
export function formatDate(date) {
|
|
8
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
9
|
+
return d.toLocaleDateString("en-US", {
|
|
10
|
+
month: "short",
|
|
11
|
+
day: "numeric",
|
|
12
|
+
year: "numeric",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format status with emoji
|
|
17
|
+
*/
|
|
18
|
+
export function formatStatus(status) {
|
|
19
|
+
const statusEmojis = {
|
|
20
|
+
// Content statuses
|
|
21
|
+
draft: "📝",
|
|
22
|
+
generating: "🔄",
|
|
23
|
+
review: "👀",
|
|
24
|
+
published: "✅",
|
|
25
|
+
archived: "📦",
|
|
26
|
+
// Brief statuses
|
|
27
|
+
pending: "⏳",
|
|
28
|
+
research: "🔍",
|
|
29
|
+
ready: "✅",
|
|
30
|
+
completed: "✅",
|
|
31
|
+
// Job statuses
|
|
32
|
+
running: "🔄",
|
|
33
|
+
failed: "❌",
|
|
34
|
+
success: "✅",
|
|
35
|
+
// Audit statuses
|
|
36
|
+
crawling: "🕷️",
|
|
37
|
+
analyzing: "🔍",
|
|
38
|
+
};
|
|
39
|
+
const emoji = statusEmojis[status.toLowerCase()] || "•";
|
|
40
|
+
return `${emoji} ${status}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Format a list of items as a markdown table
|
|
44
|
+
*/
|
|
45
|
+
export function formatTable(headers, rows, options) {
|
|
46
|
+
if (rows.length === 0) {
|
|
47
|
+
return "_No items found._";
|
|
48
|
+
}
|
|
49
|
+
const align = options?.align || headers.map(() => "left");
|
|
50
|
+
// Build header row
|
|
51
|
+
let table = "| " + headers.join(" | ") + " |\n";
|
|
52
|
+
// Build separator row with alignment
|
|
53
|
+
table +=
|
|
54
|
+
"| " +
|
|
55
|
+
align
|
|
56
|
+
.map((a) => {
|
|
57
|
+
switch (a) {
|
|
58
|
+
case "center":
|
|
59
|
+
return ":---:";
|
|
60
|
+
case "right":
|
|
61
|
+
return "---:";
|
|
62
|
+
default:
|
|
63
|
+
return "---";
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.join(" | ") +
|
|
67
|
+
" |\n";
|
|
68
|
+
// Build data rows
|
|
69
|
+
for (const row of rows) {
|
|
70
|
+
table += "| " + row.join(" | ") + " |\n";
|
|
71
|
+
}
|
|
72
|
+
return table;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Format pagination info
|
|
76
|
+
*/
|
|
77
|
+
export function formatPagination(pagination) {
|
|
78
|
+
const start = (pagination.page - 1) * pagination.page_size + 1;
|
|
79
|
+
const end = Math.min(pagination.page * pagination.page_size, pagination.total);
|
|
80
|
+
let text = `\n**Showing ${start}-${end} of ${pagination.total} items**`;
|
|
81
|
+
if (pagination.has_more) {
|
|
82
|
+
text += ` · \`has_more: true\`\n`;
|
|
83
|
+
text += `\nUse \`page: ${pagination.page + 1}\` to see more.`;
|
|
84
|
+
}
|
|
85
|
+
return text;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Format a success response
|
|
89
|
+
*/
|
|
90
|
+
export function formatSuccess(title, content) {
|
|
91
|
+
return `## ${title}\n\n${content}`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Format an error response
|
|
95
|
+
*/
|
|
96
|
+
export function formatError(error) {
|
|
97
|
+
const message = typeof error === "string" ? error : error.message;
|
|
98
|
+
return `**Error:** ${message}`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Format a key-value list
|
|
102
|
+
*/
|
|
103
|
+
export function formatKeyValue(items) {
|
|
104
|
+
const lines = [];
|
|
105
|
+
for (const [key, value] of Object.entries(items)) {
|
|
106
|
+
if (value !== undefined && value !== null) {
|
|
107
|
+
const displayKey = key
|
|
108
|
+
.replace(/_/g, " ")
|
|
109
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
110
|
+
lines.push(`- **${displayKey}:** ${value}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return lines.join("\n");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Format word count
|
|
117
|
+
*/
|
|
118
|
+
export function formatWordCount(count) {
|
|
119
|
+
if (count === null || count === undefined) {
|
|
120
|
+
return "-";
|
|
121
|
+
}
|
|
122
|
+
if (count >= 1000) {
|
|
123
|
+
return `${(count / 1000).toFixed(1)}k`;
|
|
124
|
+
}
|
|
125
|
+
return count.toString();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Truncate text with ellipsis
|
|
129
|
+
*/
|
|
130
|
+
export function truncate(text, maxLength) {
|
|
131
|
+
if (text.length <= maxLength) {
|
|
132
|
+
return text;
|
|
133
|
+
}
|
|
134
|
+
return text.slice(0, maxLength - 3) + "...";
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Frase MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A Model Context Protocol (MCP) server that allows customers to interact with
|
|
6
|
+
* their Frase account via AI tools like Claude Desktop.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - API key authentication
|
|
10
|
+
* - All v1 API functionality as MCP tools
|
|
11
|
+
* - Markdown-formatted responses for easy reading
|
|
12
|
+
* - Local caching for improved performance
|
|
13
|
+
* - Auto-retry with exponential backoff
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* Set FRASE_API_KEY environment variable before connecting
|
|
17
|
+
*
|
|
18
|
+
* Claude Desktop config example:
|
|
19
|
+
* {
|
|
20
|
+
* "mcpServers": {
|
|
21
|
+
* "frase": {
|
|
22
|
+
* "command": "npx",
|
|
23
|
+
* "args": ["-y", "@frase/mcp-server"],
|
|
24
|
+
* "env": {
|
|
25
|
+
* "FRASE_API_KEY": "your-api-key-here"
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Frase MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A Model Context Protocol (MCP) server that allows customers to interact with
|
|
6
|
+
* their Frase account via AI tools like Claude Desktop.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - API key authentication
|
|
10
|
+
* - All v1 API functionality as MCP tools
|
|
11
|
+
* - Markdown-formatted responses for easy reading
|
|
12
|
+
* - Local caching for improved performance
|
|
13
|
+
* - Auto-retry with exponential backoff
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* Set FRASE_API_KEY environment variable before connecting
|
|
17
|
+
*
|
|
18
|
+
* Claude Desktop config example:
|
|
19
|
+
* {
|
|
20
|
+
* "mcpServers": {
|
|
21
|
+
* "frase": {
|
|
22
|
+
* "command": "npx",
|
|
23
|
+
* "args": ["-y", "@frase/mcp-server"],
|
|
24
|
+
* "env": {
|
|
25
|
+
* "FRASE_API_KEY": "your-api-key-here"
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
32
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
33
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
34
|
+
import { config as loadEnv } from "dotenv";
|
|
35
|
+
// Load environment variables
|
|
36
|
+
loadEnv();
|
|
37
|
+
import { loadConfig, debugLog } from "./config.js";
|
|
38
|
+
import { FraseApiClient } from "./api-client.js";
|
|
39
|
+
import { ALL_TOOLS, TOOL_EXECUTORS, getToolByName } from "./tools/index.js";
|
|
40
|
+
import { ALL_RESOURCE_TEMPLATES, listAllResources, readResource, getRootResources, } from "./resources/index.js";
|
|
41
|
+
import { ALL_PROMPTS, getPromptByName, getPromptMessages, } from "./prompts/index.js";
|
|
42
|
+
// Server metadata
|
|
43
|
+
const SERVER_NAME = "frase-mcp-server";
|
|
44
|
+
const SERVER_VERSION = "0.1.0";
|
|
45
|
+
/**
|
|
46
|
+
* Main MCP server class
|
|
47
|
+
*/
|
|
48
|
+
class FraseMcpServer {
|
|
49
|
+
server;
|
|
50
|
+
config = null;
|
|
51
|
+
client = null;
|
|
52
|
+
isInitialized = false;
|
|
53
|
+
constructor() {
|
|
54
|
+
this.server = new Server({
|
|
55
|
+
name: SERVER_NAME,
|
|
56
|
+
version: SERVER_VERSION,
|
|
57
|
+
}, {
|
|
58
|
+
capabilities: {
|
|
59
|
+
tools: {},
|
|
60
|
+
resources: {},
|
|
61
|
+
prompts: {},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
this.setupHandlers();
|
|
65
|
+
this.setupErrorHandling();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Initialize the server (load config, create API client)
|
|
69
|
+
*/
|
|
70
|
+
async initialize() {
|
|
71
|
+
if (this.isInitialized) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
this.config = loadConfig();
|
|
76
|
+
this.client = new FraseApiClient(this.config);
|
|
77
|
+
this.isInitialized = true;
|
|
78
|
+
debugLog(this.config, "Server initialized");
|
|
79
|
+
debugLog(this.config, `API URL: ${this.config.apiUrl}`);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
83
|
+
throw new McpError(ErrorCode.InvalidRequest, message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get tool context for execution
|
|
88
|
+
*/
|
|
89
|
+
getContext() {
|
|
90
|
+
if (!this.config || !this.client) {
|
|
91
|
+
throw new McpError(ErrorCode.InvalidRequest, "Server not initialized");
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
client: this.client,
|
|
95
|
+
config: this.config,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Set up MCP request handlers
|
|
100
|
+
*/
|
|
101
|
+
setupHandlers() {
|
|
102
|
+
// Handle list_tools request
|
|
103
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
104
|
+
await this.initialize();
|
|
105
|
+
return {
|
|
106
|
+
tools: ALL_TOOLS.map((tool) => ({
|
|
107
|
+
name: tool.name,
|
|
108
|
+
description: tool.description,
|
|
109
|
+
inputSchema: tool.inputSchema,
|
|
110
|
+
})),
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
// Handle list_resources request
|
|
114
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
115
|
+
await this.initialize();
|
|
116
|
+
try {
|
|
117
|
+
const context = this.getContext();
|
|
118
|
+
const resources = await listAllResources(context);
|
|
119
|
+
return {
|
|
120
|
+
resources: resources.map((r) => ({
|
|
121
|
+
uri: r.uri,
|
|
122
|
+
name: r.name,
|
|
123
|
+
description: r.description,
|
|
124
|
+
mimeType: r.mimeType,
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error("[Frase MCP] Error listing resources:", error);
|
|
130
|
+
// Return root resources as fallback
|
|
131
|
+
return {
|
|
132
|
+
resources: getRootResources().map((r) => ({
|
|
133
|
+
uri: r.uri,
|
|
134
|
+
name: r.name,
|
|
135
|
+
description: r.description,
|
|
136
|
+
mimeType: r.mimeType,
|
|
137
|
+
})),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
// Handle list_resource_templates request
|
|
142
|
+
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
143
|
+
return {
|
|
144
|
+
resourceTemplates: ALL_RESOURCE_TEMPLATES.map((t) => ({
|
|
145
|
+
uriTemplate: t.uriTemplate,
|
|
146
|
+
name: t.name,
|
|
147
|
+
description: t.description,
|
|
148
|
+
mimeType: t.mimeType,
|
|
149
|
+
})),
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
// Handle read_resource request
|
|
153
|
+
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
154
|
+
await this.initialize();
|
|
155
|
+
const { uri } = request.params;
|
|
156
|
+
try {
|
|
157
|
+
debugLog(this.config, `Reading resource: ${uri}`);
|
|
158
|
+
const context = this.getContext();
|
|
159
|
+
const result = await readResource(uri, context);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
console.error(`[Frase MCP] Error reading resource:`, error);
|
|
164
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
165
|
+
throw new McpError(ErrorCode.InvalidRequest, message);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
// Handle list_prompts request
|
|
169
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
170
|
+
return {
|
|
171
|
+
prompts: ALL_PROMPTS.map((p) => ({
|
|
172
|
+
name: p.name,
|
|
173
|
+
description: p.description,
|
|
174
|
+
arguments: p.arguments,
|
|
175
|
+
})),
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
// Handle get_prompt request
|
|
179
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
180
|
+
const { name, arguments: promptArgs } = request.params;
|
|
181
|
+
const prompt = getPromptByName(name);
|
|
182
|
+
if (!prompt) {
|
|
183
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown prompt: ${name}`);
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const messages = getPromptMessages(name, promptArgs || {});
|
|
187
|
+
return {
|
|
188
|
+
description: prompt.description,
|
|
189
|
+
messages,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
console.error(`[Frase MCP] Error getting prompt:`, error);
|
|
194
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
195
|
+
throw new McpError(ErrorCode.InvalidRequest, message);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
// Handle call_tool request
|
|
199
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
200
|
+
await this.initialize();
|
|
201
|
+
const { name, arguments: args } = request.params;
|
|
202
|
+
// Find the tool
|
|
203
|
+
const tool = getToolByName(name);
|
|
204
|
+
if (!tool) {
|
|
205
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
206
|
+
}
|
|
207
|
+
// Get the executor
|
|
208
|
+
const executor = TOOL_EXECUTORS[name];
|
|
209
|
+
if (!executor) {
|
|
210
|
+
throw new McpError(ErrorCode.InternalError, `No executor for tool: ${name}`);
|
|
211
|
+
}
|
|
212
|
+
// Execute the tool
|
|
213
|
+
try {
|
|
214
|
+
debugLog(this.config, `Executing tool: ${name}`);
|
|
215
|
+
const startTime = Date.now();
|
|
216
|
+
const context = this.getContext();
|
|
217
|
+
const result = await executor(args, context);
|
|
218
|
+
const duration = Date.now() - startTime;
|
|
219
|
+
debugLog(this.config, `Tool ${name} completed in ${duration}ms`);
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: result.markdown,
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
isError: !result.success,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error(`[Frase MCP] Tool execution error:`, error);
|
|
232
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
233
|
+
return {
|
|
234
|
+
content: [
|
|
235
|
+
{
|
|
236
|
+
type: "text",
|
|
237
|
+
text: `**Error:** ${message}`,
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
isError: true,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Set up error handling
|
|
247
|
+
*/
|
|
248
|
+
setupErrorHandling() {
|
|
249
|
+
this.server.onerror = (error) => {
|
|
250
|
+
console.error("[Frase MCP] Server error:", error);
|
|
251
|
+
};
|
|
252
|
+
process.on("SIGINT", async () => {
|
|
253
|
+
console.error("[Frase MCP] Shutting down...");
|
|
254
|
+
this.cleanup();
|
|
255
|
+
await this.server.close();
|
|
256
|
+
process.exit(0);
|
|
257
|
+
});
|
|
258
|
+
process.on("SIGTERM", async () => {
|
|
259
|
+
console.error("[Frase MCP] Shutting down...");
|
|
260
|
+
this.cleanup();
|
|
261
|
+
await this.server.close();
|
|
262
|
+
process.exit(0);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Clean up resources
|
|
267
|
+
*/
|
|
268
|
+
cleanup() {
|
|
269
|
+
if (this.client) {
|
|
270
|
+
this.client.destroy();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Start the MCP server
|
|
275
|
+
*/
|
|
276
|
+
async run() {
|
|
277
|
+
const transport = new StdioServerTransport();
|
|
278
|
+
await this.server.connect(transport);
|
|
279
|
+
console.error(`[Frase MCP] Server started (v${SERVER_VERSION})`);
|
|
280
|
+
console.error(`[Frase MCP] Tools available: ${ALL_TOOLS.length}`);
|
|
281
|
+
console.error(`[Frase MCP] Resource templates: ${ALL_RESOURCE_TEMPLATES.length}`);
|
|
282
|
+
console.error(`[Frase MCP] Prompts available: ${ALL_PROMPTS.length}`);
|
|
283
|
+
console.error("[Frase MCP] Waiting for connection...");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Entry point
|
|
287
|
+
const server = new FraseMcpServer();
|
|
288
|
+
server.run().catch((error) => {
|
|
289
|
+
console.error("[Frase MCP] Fatal error:", error);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
});
|
|
292
|
+
//# sourceMappingURL=index.js.map
|