@bulkpublishing/mcp-server 1.0.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/README.md +170 -0
- package/dist/ai/engine.d.ts +12 -0
- package/dist/ai/engine.d.ts.map +1 -0
- package/dist/ai/engine.js +397 -0
- package/dist/ai/engine.js.map +1 -0
- package/dist/ai/prompts.d.ts +30 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +207 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/auth/context.d.ts +81 -0
- package/dist/auth/context.d.ts.map +1 -0
- package/dist/auth/context.js +68 -0
- package/dist/auth/context.js.map +1 -0
- package/dist/auth/validate.d.ts +13 -0
- package/dist/auth/validate.d.ts.map +1 -0
- package/dist/auth/validate.js +87 -0
- package/dist/auth/validate.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/csv.d.ts +18 -0
- package/dist/tools/csv.d.ts.map +1 -0
- package/dist/tools/csv.js +673 -0
- package/dist/tools/csv.js.map +1 -0
- package/dist/tools/generation.d.ts +14 -0
- package/dist/tools/generation.d.ts.map +1 -0
- package/dist/tools/generation.js +291 -0
- package/dist/tools/generation.js.map +1 -0
- package/dist/tools/indexing.d.ts +11 -0
- package/dist/tools/indexing.d.ts.map +1 -0
- package/dist/tools/indexing.js +219 -0
- package/dist/tools/indexing.js.map +1 -0
- package/dist/tools/projects.d.ts +11 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +181 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/research.d.ts +12 -0
- package/dist/tools/research.d.ts.map +1 -0
- package/dist/tools/research.js +88 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/tools/seo.d.ts +12 -0
- package/dist/tools/seo.d.ts.map +1 -0
- package/dist/tools/seo.js +164 -0
- package/dist/tools/seo.js.map +1 -0
- package/dist/tools/wordpress.d.ts +15 -0
- package/dist/tools/wordpress.d.ts.map +1 -0
- package/dist/tools/wordpress.js +447 -0
- package/dist/tools/wordpress.js.map +1 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BPAI MCP Server — Prompt Templates
|
|
3
|
+
*
|
|
4
|
+
* Adapted from src/services/ai/prompts.ts for server-side MCP usage.
|
|
5
|
+
* Contains the master generation prompt, humanizer, SEO metadata generator,
|
|
6
|
+
* and internal linking builder.
|
|
7
|
+
*/
|
|
8
|
+
// ============================================
|
|
9
|
+
// ARTICLE GENERATION PROMPT
|
|
10
|
+
// ============================================
|
|
11
|
+
export const ROBUST_PROMPT_TEMPLATE = `
|
|
12
|
+
(Act as an expert SEO content writer. Create a comprehensive, high-ranking blog post for the keyword: "{{keyword}}".
|
|
13
|
+
|
|
14
|
+
⚠️ CRITICAL OUTPUT RULE: Return ONLY raw Markdown text. NEVER return JSON. NEVER wrap your response in { } braces or a code block.
|
|
15
|
+
|
|
16
|
+
KEYWORD INSTRUCTIONS:
|
|
17
|
+
1. Target Keyword: {{keyword}} (Use natural variations, do NOT overstuff).
|
|
18
|
+
2. Secondary Keywords: Identify and naturally integrate 3-5 LSI/secondary keywords relevant to the topic to improve semantic reach.
|
|
19
|
+
3. Slug: Use the exact target keyword as the slug, lowercased and hyphenated.
|
|
20
|
+
4. **KEYWORD PLACEMENT (STRICT):**
|
|
21
|
+
- The exact target keyword MUST appear in the first 25% of the article (intro or first section).
|
|
22
|
+
- The exact target keyword MUST appear in at least one H2 header near the beginning of the article.
|
|
23
|
+
|
|
24
|
+
{{template_instruction}}
|
|
25
|
+
|
|
26
|
+
DATA & CONTEXT INTEGRITY:
|
|
27
|
+
You will be provided with "Context" or "Research Data" in the user prompt. You MUST prioritize this data for facts, statistics, and specific details. Do NOT simulate a web search. Use the provided real-world data to back your claims.
|
|
28
|
+
|
|
29
|
+
{{title_instruction}}
|
|
30
|
+
{{h1_instruction}}
|
|
31
|
+
|
|
32
|
+
OUTPUT FORMAT (MANDATORY):
|
|
33
|
+
YOU MUST RETURN RAW MARKDOWN TEXT ONLY. DO NOT RETURN JSON.
|
|
34
|
+
- Start directly with the title using # (H1).
|
|
35
|
+
- Use ## for main sections (H2).
|
|
36
|
+
- Use ### for subsections (H3).
|
|
37
|
+
- Use tables, lists, and bold text for readability.
|
|
38
|
+
- NO JSON format. NO code blocks. NO { "title": } structures. Just raw markdown text.
|
|
39
|
+
- At the very END of the article, add the meta description in this format:
|
|
40
|
+
META_DESCRIPTION: [Your 155-char max meta description here]
|
|
41
|
+
|
|
42
|
+
CONTENT SEPARATION (CRITICAL):
|
|
43
|
+
- The article body must contain ONLY the article content itself.
|
|
44
|
+
- DO NOT include any "Meta title", "Slug", "Title Tag", or metadata sections inside the article.
|
|
45
|
+
- Meta titles, slugs, and meta descriptions are handled SEPARATELY by the system.
|
|
46
|
+
- Your output = H1 title + article body + META_DESCRIPTION line at the end. Nothing else.
|
|
47
|
+
|
|
48
|
+
REQUIREMENTS:
|
|
49
|
+
1. Title & Meta: Title under 60 characters. Meta description 150-160 characters.
|
|
50
|
+
2. Content Structure (INVERTED PYRAMID): Give the answer immediately. Surface answers first, details later. Remove fluff.
|
|
51
|
+
3. Readability: Simple, accessible language. Paragraphs 2-3 sentences max. Frequent visual breaks.
|
|
52
|
+
4. TL;DR: 3-4 bullet points (~75 words) that fully answer the user's query.
|
|
53
|
+
5. Intro: ~50-100 words, keyword mentioned, direct answer visible.
|
|
54
|
+
|
|
55
|
+
6. INTRO OPENER (Use Opener Seed: {{opener_seed}}):
|
|
56
|
+
Strategy A — COLD STAT OPENER: Lead with a surprising number.
|
|
57
|
+
Strategy B — CONTRARIAN CLAIM: Bold, slightly provocative statement.
|
|
58
|
+
Strategy C — MINI-STORY: A single vivid sentence.
|
|
59
|
+
Strategy D — DIRECT ANSWER FIRST: Answer the query in sentence one.
|
|
60
|
+
Strategy E — QUESTION HOOK: One sharp question.
|
|
61
|
+
Strategy F — TENSION/PROBLEM: Name the specific pain.
|
|
62
|
+
Strategy G — QUOTE OR ANECDOTE: A real quote or 1-line anecdote.
|
|
63
|
+
Strategy H — BEFORE/AFTER CONTRAST: Transformation in one sentence.
|
|
64
|
+
|
|
65
|
+
7. Components: DEPTH & EXPANSION. Explain concepts in depth. Include at least one data table.
|
|
66
|
+
{{internal_linking_instructions}}
|
|
67
|
+
|
|
68
|
+
8. CRITICAL RULES:
|
|
69
|
+
- THE CURRENT YEAR IS 2026.
|
|
70
|
+
- NEVER use em-dashes.
|
|
71
|
+
- Use clear headings (H1, H2, H3).
|
|
72
|
+
- Include tables, lists, bold text for readability.
|
|
73
|
+
- Mention specific brands, models, features, and pricing tiers.
|
|
74
|
+
- Reference external sources with natural anchor text (NOT [1] bracket numbers).
|
|
75
|
+
- NEVER fabricate URLs.
|
|
76
|
+
|
|
77
|
+
9. FORBIDDEN AI TERMINOLOGY:
|
|
78
|
+
Never use: delve, comprehensive, leverage, harness, seamlessly, robust, landscape, multifaceted, pivotal, realm, intricate, tapestry, synergy, ecosystem, embark, embrace, illuminate, paradigm, culmination, quest, pinnacle, beacon, foster, catalyze, meticulous, nuanced, transformative, holistic, resonate, underscore, underpins, streamline, incorporate, facilitate, encompass, exemplify, epitomize, and all related AI-sounding words.
|
|
79
|
+
|
|
80
|
+
10. BANNED STRUCTURAL PATTERNS:
|
|
81
|
+
- Three consecutive short "You" sentences.
|
|
82
|
+
- Starting sentences with "By" + gerund.
|
|
83
|
+
- "it's not about X, it's about Y" patterns.
|
|
84
|
+
- Generic closers ("And that's why X matters.").
|
|
85
|
+
- Motivational speech tone.
|
|
86
|
+
`;
|
|
87
|
+
// ============================================
|
|
88
|
+
// SEO METADATA GENERATOR
|
|
89
|
+
// ============================================
|
|
90
|
+
export const SEO_GENERATOR_PROMPT = `You are an expert SEO strategist. Generate optimized metadata for a blog article.
|
|
91
|
+
|
|
92
|
+
CRITICAL: Return ONLY valid JSON. No explanations, no markdown, no code blocks.
|
|
93
|
+
|
|
94
|
+
For the given topic/keyword, return this EXACT JSON structure:
|
|
95
|
+
{
|
|
96
|
+
"title": "Article H1 heading",
|
|
97
|
+
"titleTag": "SEO title for search results (50-60 chars max)",
|
|
98
|
+
"slug": "url-friendly-slug",
|
|
99
|
+
"mainKeyword": "primary target keyword phrase",
|
|
100
|
+
"secondaryKeywords": "related, lsi, keywords",
|
|
101
|
+
"metaDescription": "Action-oriented meta description (150-160 chars)"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Rules:
|
|
105
|
+
- THE YEAR IS 2026
|
|
106
|
+
- No forbidden AI words
|
|
107
|
+
- Meta description must lead with a verb
|
|
108
|
+
- Slug is keyword lowercased and hyphenated`;
|
|
109
|
+
// ============================================
|
|
110
|
+
// HUMANIZE PROMPT
|
|
111
|
+
// ============================================
|
|
112
|
+
export const HUMANIZE_PROMPT = `
|
|
113
|
+
You are a senior human editor performing a FINAL anti-AI detection pass on a blog article.
|
|
114
|
+
Your job is to rewrite any sentence or section that reads like AI-generated text, while preserving every fact, link, and piece of data.
|
|
115
|
+
|
|
116
|
+
INPUT CONTENT:
|
|
117
|
+
{{content}}
|
|
118
|
+
|
|
119
|
+
=== KEY RULES ===
|
|
120
|
+
1. Replace all banned AI words (delve, comprehensive, leverage, harness, etc.)
|
|
121
|
+
2. Replace all banned phrases ("in today's", "it's important to note", etc.)
|
|
122
|
+
3. Remove ALL em-dashes and replace with periods, commas, or semicolons
|
|
123
|
+
4. Mix sentence lengths (short punchy + medium)
|
|
124
|
+
5. No triplet patterns (3+ sentences starting with same word)
|
|
125
|
+
6. No "By + gerund" sentence starters
|
|
126
|
+
7. Use contractions (don't, it's, they're)
|
|
127
|
+
8. Vary paragraph openings
|
|
128
|
+
9. Preserve ALL links, data, formatting, and content depth
|
|
129
|
+
10. Output must be at least as long as input
|
|
130
|
+
|
|
131
|
+
=== OUTPUT ===
|
|
132
|
+
Return the FULL humanized article in Markdown. Nothing else.
|
|
133
|
+
`;
|
|
134
|
+
// ============================================
|
|
135
|
+
// INTERNAL LINKING BUILDER
|
|
136
|
+
// ============================================
|
|
137
|
+
export function buildInternalLinkingPrompt(urls, maxLinks) {
|
|
138
|
+
if (!urls || urls.length === 0)
|
|
139
|
+
return '';
|
|
140
|
+
const linkCount = Math.min(Math.max(maxLinks || 3, 1), 10);
|
|
141
|
+
// Shuffle for variety across articles
|
|
142
|
+
const shuffled = [...urls];
|
|
143
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
144
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
145
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
146
|
+
}
|
|
147
|
+
const limited = shuffled.slice(0, 150);
|
|
148
|
+
return `
|
|
149
|
+
=== INTERNAL LINKING REQUIREMENTS (MANDATORY) ===
|
|
150
|
+
You MUST include EXACTLY ${linkCount} internal links to other pages on this site.
|
|
151
|
+
|
|
152
|
+
INSTRUCTIONS:
|
|
153
|
+
1. Choose ${linkCount} URLs from the "Candidate URLs" list below.
|
|
154
|
+
2. Insert them NATURALLY into the content proper.
|
|
155
|
+
3. DO NOT create a "Related Posts" section. The links must be inside the paragraphs.
|
|
156
|
+
4. Use DESCRIPTIVE anchor text.
|
|
157
|
+
5. Distribute the links evenly throughout the article.
|
|
158
|
+
|
|
159
|
+
CANDIDATE URLS (Choose ${linkCount} from this list):
|
|
160
|
+
${limited.map(url => ` - ${url}`).join('\n')}
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
// ============================================
|
|
164
|
+
// RESEARCH PROMPT
|
|
165
|
+
// ============================================
|
|
166
|
+
export const RESEARCH_PROMPT = `You are a research assistant. Search the web for accurate, current information about the given topic.
|
|
167
|
+
|
|
168
|
+
Focus on:
|
|
169
|
+
1. Recent data, statistics, and trends (2025-2026)
|
|
170
|
+
2. Expert opinions and quotes
|
|
171
|
+
3. Specific product names, pricing, and features
|
|
172
|
+
4. Competitor comparisons
|
|
173
|
+
5. Common questions people ask about this topic
|
|
174
|
+
|
|
175
|
+
Return your findings as a structured research brief with citations. Include source URLs where available.`;
|
|
176
|
+
// ============================================
|
|
177
|
+
// PROMPT BUILDER HELPERS
|
|
178
|
+
// ============================================
|
|
179
|
+
const OPENER_STRATEGIES = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
|
180
|
+
export function buildGenerationPrompt(options) {
|
|
181
|
+
const openerSeed = OPENER_STRATEGIES[(options.openerSeedIndex || Math.floor(Math.random() * 8)) % 8];
|
|
182
|
+
let systemPrompt = ROBUST_PROMPT_TEMPLATE
|
|
183
|
+
.replace(/\{\{keyword\}\}/g, options.keyword)
|
|
184
|
+
.replace('{{opener_seed}}', openerSeed)
|
|
185
|
+
.replace('{{template_instruction}}', options.templateInstruction || '')
|
|
186
|
+
.replace('{{title_instruction}}', options.titleInstruction || '')
|
|
187
|
+
.replace('{{h1_instruction}}', options.h1Instruction || '')
|
|
188
|
+
.replace('{{internal_linking_instructions}}', options.internalLinkUrls
|
|
189
|
+
? buildInternalLinkingPrompt(options.internalLinkUrls, options.maxInternalLinks || 5)
|
|
190
|
+
: '');
|
|
191
|
+
// Build user prompt
|
|
192
|
+
let userPrompt = `Write a ${options.wordCount || 2500}-word article about "${options.keyword}".`;
|
|
193
|
+
if (options.tone) {
|
|
194
|
+
userPrompt += `\n\nTone: ${options.tone}`;
|
|
195
|
+
}
|
|
196
|
+
if (options.knowledgeBaseContext) {
|
|
197
|
+
systemPrompt = `${options.knowledgeBaseContext}\n\n${systemPrompt}`;
|
|
198
|
+
}
|
|
199
|
+
if (options.researchContext) {
|
|
200
|
+
userPrompt += `\n\n=== RESEARCH DATA (USE THIS AS PRIMARY SOURCE) ===\n${options.researchContext}`;
|
|
201
|
+
}
|
|
202
|
+
if (options.ctaSnippets && options.ctaSnippets.length > 0) {
|
|
203
|
+
userPrompt += `\n\n=== CTA BLOCKS (Insert these naturally into the article) ===\n${options.ctaSnippets.join('\n\n')}`;
|
|
204
|
+
}
|
|
205
|
+
return { systemPrompt, userPrompt };
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/ai/prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server — User Context
|
|
3
|
+
*
|
|
4
|
+
* Holds the authenticated user's full context after token validation.
|
|
5
|
+
* All tools read from this singleton instead of requiring inline keys.
|
|
6
|
+
*/
|
|
7
|
+
import type { ApiKeys } from '../types.js';
|
|
8
|
+
export interface UserContext {
|
|
9
|
+
authenticated: boolean;
|
|
10
|
+
user: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
email: string;
|
|
14
|
+
};
|
|
15
|
+
settings: {
|
|
16
|
+
api_keys: ApiKeys;
|
|
17
|
+
model_settings: Record<string, any>;
|
|
18
|
+
global_prompts: {
|
|
19
|
+
strategy?: string;
|
|
20
|
+
customSystem?: string;
|
|
21
|
+
masterSystem?: string;
|
|
22
|
+
userTemplate?: string;
|
|
23
|
+
additionalInstructions?: string;
|
|
24
|
+
};
|
|
25
|
+
multi_key_config: Record<string, any>;
|
|
26
|
+
master_prompt: string | null;
|
|
27
|
+
};
|
|
28
|
+
projects: ProjectContext[];
|
|
29
|
+
activeProjectId: string | null;
|
|
30
|
+
knowledgeBase: KBItem[];
|
|
31
|
+
}
|
|
32
|
+
export interface ProjectContext {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
color?: string;
|
|
37
|
+
created_at: string;
|
|
38
|
+
settings: {
|
|
39
|
+
internal_linking?: {
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
sitemapUrls: string[];
|
|
42
|
+
maxLinksPerPost: number;
|
|
43
|
+
sourceUrl?: string;
|
|
44
|
+
includedPaths?: string[];
|
|
45
|
+
};
|
|
46
|
+
cross_linking?: {
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
baseUrl: string;
|
|
49
|
+
maxLinksPerPost: number;
|
|
50
|
+
};
|
|
51
|
+
wordpress?: {
|
|
52
|
+
connected: boolean;
|
|
53
|
+
siteUrl: string;
|
|
54
|
+
username: string;
|
|
55
|
+
appPassword: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export interface KBItem {
|
|
60
|
+
id: string;
|
|
61
|
+
title: string;
|
|
62
|
+
content: string;
|
|
63
|
+
project_id: string | null;
|
|
64
|
+
type: string;
|
|
65
|
+
}
|
|
66
|
+
export declare function setUserContext(ctx: UserContext): void;
|
|
67
|
+
export declare function getUserContext(): UserContext;
|
|
68
|
+
/**
|
|
69
|
+
* Get the API keys for a specific provider.
|
|
70
|
+
* Falls back to env vars if the user's stored key is empty.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getApiKey(provider: string): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Get the active project's settings, or a specific project by ID.
|
|
75
|
+
*/
|
|
76
|
+
export declare function getProjectSettings(projectId?: string): ProjectContext | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Get knowledge base items for the active project (includes global items).
|
|
79
|
+
*/
|
|
80
|
+
export declare function getKnowledgeBaseContext(projectId?: string): string;
|
|
81
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/auth/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,WAAW;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE;QACF,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,EAAE;QACN,QAAQ,EAAE,OAAO,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACpC,cAAc,EAAE;YACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,YAAY,CAAC,EAAE,MAAM,CAAC;YACtB,sBAAsB,CAAC,EAAE,MAAM,CAAC;SACnC,CAAC;QACF,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACtC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;IACF,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE;QACN,gBAAgB,CAAC,EAAE;YACf,OAAO,EAAE,OAAO,CAAC;YACjB,WAAW,EAAE,MAAM,EAAE,CAAC;YACtB,eAAe,EAAE,MAAM,CAAC;YACxB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;SAC5B,CAAC;QACF,aAAa,CAAC,EAAE;YACZ,OAAO,EAAE,OAAO,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,eAAe,EAAE,MAAM,CAAC;SAC3B,CAAC;QACF,SAAS,CAAC,EAAE;YACR,SAAS,EAAE,OAAO,CAAC;YACnB,OAAO,EAAE,MAAM,CAAC;YAChB,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,EAAE,MAAM,CAAC;SACvB,CAAC;KACL,CAAC;CACL;AAED,MAAM,WAAW,MAAM;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CAChB;AAkBD,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAErD;AAED,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAIjF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CASlE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server — User Context
|
|
3
|
+
*
|
|
4
|
+
* Holds the authenticated user's full context after token validation.
|
|
5
|
+
* All tools read from this singleton instead of requiring inline keys.
|
|
6
|
+
*/
|
|
7
|
+
// Singleton — set once on startup, read by all tools
|
|
8
|
+
let _context = {
|
|
9
|
+
authenticated: false,
|
|
10
|
+
user: { id: '', name: '', email: '' },
|
|
11
|
+
settings: {
|
|
12
|
+
api_keys: {},
|
|
13
|
+
model_settings: {},
|
|
14
|
+
global_prompts: {},
|
|
15
|
+
multi_key_config: {},
|
|
16
|
+
master_prompt: null,
|
|
17
|
+
},
|
|
18
|
+
projects: [],
|
|
19
|
+
activeProjectId: null,
|
|
20
|
+
knowledgeBase: [],
|
|
21
|
+
};
|
|
22
|
+
export function setUserContext(ctx) {
|
|
23
|
+
_context = ctx;
|
|
24
|
+
}
|
|
25
|
+
export function getUserContext() {
|
|
26
|
+
return _context;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the API keys for a specific provider.
|
|
30
|
+
* Falls back to env vars if the user's stored key is empty.
|
|
31
|
+
*/
|
|
32
|
+
export function getApiKey(provider) {
|
|
33
|
+
const keys = _context.settings.api_keys;
|
|
34
|
+
const stored = keys[provider];
|
|
35
|
+
if (stored)
|
|
36
|
+
return stored;
|
|
37
|
+
// Fallback to environment variables
|
|
38
|
+
const envMap = {
|
|
39
|
+
openai: 'OPENAI_API_KEY',
|
|
40
|
+
claude: 'CLAUDE_API_KEY',
|
|
41
|
+
deepseek: 'DEEPSEEK_API_KEY',
|
|
42
|
+
grok: 'GROK_API_KEY',
|
|
43
|
+
gemini: 'GEMINI_API_KEY',
|
|
44
|
+
perplexity: 'PERPLEXITY_API_KEY',
|
|
45
|
+
kimi: 'KIMI_API_KEY',
|
|
46
|
+
};
|
|
47
|
+
return process.env[envMap[provider] || ''];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the active project's settings, or a specific project by ID.
|
|
51
|
+
*/
|
|
52
|
+
export function getProjectSettings(projectId) {
|
|
53
|
+
const targetId = projectId || _context.activeProjectId;
|
|
54
|
+
if (!targetId)
|
|
55
|
+
return _context.projects[0]; // fallback to first project
|
|
56
|
+
return _context.projects.find(p => p.id === targetId);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get knowledge base items for the active project (includes global items).
|
|
60
|
+
*/
|
|
61
|
+
export function getKnowledgeBaseContext(projectId) {
|
|
62
|
+
const targetId = projectId || _context.activeProjectId;
|
|
63
|
+
const items = _context.knowledgeBase.filter(kb => kb.project_id === targetId || kb.project_id === null);
|
|
64
|
+
if (items.length === 0)
|
|
65
|
+
return '';
|
|
66
|
+
return items.map(kb => `[${kb.type || 'context'}] ${kb.title}:\n${kb.content}`).join('\n\n---\n\n');
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/auth/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiEH,qDAAqD;AACrD,IAAI,QAAQ,GAAgB;IACxB,aAAa,EAAE,KAAK;IACpB,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;IACrC,QAAQ,EAAE;QACN,QAAQ,EAAE,EAAE;QACZ,cAAc,EAAE,EAAE;QAClB,cAAc,EAAE,EAAE;QAClB,gBAAgB,EAAE,EAAE;QACpB,aAAa,EAAE,IAAI;KACtB;IACD,QAAQ,EAAE,EAAE;IACZ,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,EAAE;CACpB,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,GAAgB;IAC3C,QAAQ,GAAG,GAAG,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc;IAC1B,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAkC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,oCAAoC;IACpC,MAAM,MAAM,GAA2B;QACnC,MAAM,EAAE,gBAAgB;QACxB,MAAM,EAAE,gBAAgB;QACxB,QAAQ,EAAE,kBAAkB;QAC5B,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,gBAAgB;QACxB,UAAU,EAAE,oBAAoB;QAChC,IAAI,EAAE,cAAc;KACvB,CAAC;IAEF,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAkB;IACjD,MAAM,QAAQ,GAAG,SAAS,IAAI,QAAQ,CAAC,eAAe,CAAC;IACvD,IAAI,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,4BAA4B;IACxE,OAAO,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAkB;IACtD,MAAM,QAAQ,GAAG,SAAS,IAAI,QAAQ,CAAC,eAAe,CAAC;IACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CACvC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,KAAK,QAAQ,IAAI,EAAE,CAAC,UAAU,KAAK,IAAI,CAC7D,CAAC;IAEF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,OAAO,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,IAAI,SAAS,KAAK,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACxG,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server — Token Validation
|
|
3
|
+
*
|
|
4
|
+
* On startup, validates the BPAI_API_KEY against the auth edge function,
|
|
5
|
+
* loads the full user context, and makes it available to all tools.
|
|
6
|
+
*/
|
|
7
|
+
import type { UserContext } from './context.js';
|
|
8
|
+
/**
|
|
9
|
+
* Validate the BPAI API key and load user context.
|
|
10
|
+
* Called once on server startup. Throws on failure.
|
|
11
|
+
*/
|
|
12
|
+
export declare function authenticate(): Promise<UserContext>;
|
|
13
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/auth/validate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,cAAc,CAAC;AAKxE;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CA2FzD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server — Token Validation
|
|
3
|
+
*
|
|
4
|
+
* On startup, validates the BPAI_API_KEY against the auth edge function,
|
|
5
|
+
* loads the full user context, and makes it available to all tools.
|
|
6
|
+
*/
|
|
7
|
+
import { setUserContext } from './context.js';
|
|
8
|
+
// The auth endpoint deployed on the BPAI Netlify site
|
|
9
|
+
const AUTH_ENDPOINT = process.env.BPAI_AUTH_URL || 'https://bulkpublishing.ai/api/mcp-auth';
|
|
10
|
+
/**
|
|
11
|
+
* Validate the BPAI API key and load user context.
|
|
12
|
+
* Called once on server startup. Throws on failure.
|
|
13
|
+
*/
|
|
14
|
+
export async function authenticate() {
|
|
15
|
+
const apiKey = process.env.BPAI_API_KEY;
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
throw new Error('BPAI_API_KEY not set. Generate your MCP API key at bulkpublishing.ai → Settings → API Access, ' +
|
|
18
|
+
'then add it to your MCP client config as BPAI_API_KEY.');
|
|
19
|
+
}
|
|
20
|
+
console.error('[Auth] Validating API key...');
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(AUTH_ENDPOINT, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const body = await response.json().catch(() => ({}));
|
|
31
|
+
const message = body.message || body.error || `HTTP ${response.status}`;
|
|
32
|
+
if (response.status === 401) {
|
|
33
|
+
throw new Error(`Authentication failed: ${message}. ` +
|
|
34
|
+
'Check that your BPAI_API_KEY is correct and not expired.');
|
|
35
|
+
}
|
|
36
|
+
if (response.status === 403) {
|
|
37
|
+
throw new Error(`Subscription inactive: ${message}. ` +
|
|
38
|
+
'Visit bulkpublishing.ai/checkout to reactivate your plan.');
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Auth endpoint error: ${message}`);
|
|
41
|
+
}
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
// Build user context from response
|
|
44
|
+
const context = {
|
|
45
|
+
authenticated: true,
|
|
46
|
+
user: data.user,
|
|
47
|
+
settings: {
|
|
48
|
+
api_keys: data.settings.api_keys || {},
|
|
49
|
+
model_settings: data.settings.model_settings || {},
|
|
50
|
+
global_prompts: data.settings.global_prompts || {},
|
|
51
|
+
multi_key_config: data.settings.multi_key_config || {},
|
|
52
|
+
master_prompt: data.settings.master_prompt || null,
|
|
53
|
+
},
|
|
54
|
+
projects: (data.projects || []).map((p) => ({
|
|
55
|
+
id: p.id,
|
|
56
|
+
name: p.name,
|
|
57
|
+
description: p.description,
|
|
58
|
+
color: p.color,
|
|
59
|
+
created_at: p.created_at,
|
|
60
|
+
settings: p.settings || {},
|
|
61
|
+
})),
|
|
62
|
+
activeProjectId: data.projects?.[0]?.id || null,
|
|
63
|
+
knowledgeBase: (data.knowledge_base || []).map((kb) => ({
|
|
64
|
+
id: kb.id,
|
|
65
|
+
title: kb.title,
|
|
66
|
+
content: kb.content,
|
|
67
|
+
project_id: kb.project_id,
|
|
68
|
+
type: kb.type,
|
|
69
|
+
})),
|
|
70
|
+
};
|
|
71
|
+
// Store in singleton
|
|
72
|
+
setUserContext(context);
|
|
73
|
+
console.error(`[Auth] Authenticated as ${context.user.name} (${context.user.email})`);
|
|
74
|
+
console.error(`[Auth] ${context.projects.length} projects loaded`);
|
|
75
|
+
console.error(`[Auth] ${context.knowledgeBase.length} knowledge base items loaded`);
|
|
76
|
+
console.error(`[Auth] Providers with keys: ${Object.keys(context.settings.api_keys).filter(k => context.settings.api_keys[k]).join(', ') || 'none (use env vars)'}`);
|
|
77
|
+
return context;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error.message.includes('fetch failed') || error.cause?.code === 'ENOTFOUND') {
|
|
81
|
+
throw new Error('Could not reach the BPAI auth server. Check your internet connection. ' +
|
|
82
|
+
'If the issue persists, visit status.bulkpublishing.ai for service status.');
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/auth/validate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bulk Publishing AI — MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A Model Context Protocol server that exposes BPAI's content generation,
|
|
6
|
+
* WordPress publishing, SEO, and indexing capabilities as tools callable
|
|
7
|
+
* from any MCP-compatible client (Claude Desktop, Antigravity, Cursor, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Transport: stdio (standard MCP transport)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node dist/index.js
|
|
13
|
+
* npx tsx src/index.ts (development)
|
|
14
|
+
*
|
|
15
|
+
* Required environment variables:
|
|
16
|
+
* BPAI_API_KEY — Your Bulk Publishing AI API key (from Settings → API Access)
|
|
17
|
+
*
|
|
18
|
+
* Optional overrides:
|
|
19
|
+
* BPAI_AUTH_URL — Custom auth endpoint (default: https://bulkpublishing.ai/api/mcp-auth)
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;GAkBG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bulk Publishing AI — MCP Server
|
|
4
|
+
*
|
|
5
|
+
* A Model Context Protocol server that exposes BPAI's content generation,
|
|
6
|
+
* WordPress publishing, SEO, and indexing capabilities as tools callable
|
|
7
|
+
* from any MCP-compatible client (Claude Desktop, Antigravity, Cursor, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Transport: stdio (standard MCP transport)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node dist/index.js
|
|
13
|
+
* npx tsx src/index.ts (development)
|
|
14
|
+
*
|
|
15
|
+
* Required environment variables:
|
|
16
|
+
* BPAI_API_KEY — Your Bulk Publishing AI API key (from Settings → API Access)
|
|
17
|
+
*
|
|
18
|
+
* Optional overrides:
|
|
19
|
+
* BPAI_AUTH_URL — Custom auth endpoint (default: https://bulkpublishing.ai/api/mcp-auth)
|
|
20
|
+
*/
|
|
21
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
22
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
23
|
+
// Auth
|
|
24
|
+
import { authenticate } from './auth/validate.js';
|
|
25
|
+
// Tool registrations
|
|
26
|
+
import { registerGenerationTools } from './tools/generation.js';
|
|
27
|
+
import { registerResearchTools } from './tools/research.js';
|
|
28
|
+
import { registerWordPressTools } from './tools/wordpress.js';
|
|
29
|
+
import { registerIndexingTools } from './tools/indexing.js';
|
|
30
|
+
import { registerSEOTools } from './tools/seo.js';
|
|
31
|
+
import { registerProjectTools } from './tools/projects.js';
|
|
32
|
+
import { registerCSVTools } from './tools/csv.js';
|
|
33
|
+
// ============================================
|
|
34
|
+
// SERVER SETUP
|
|
35
|
+
// ============================================
|
|
36
|
+
const server = new McpServer({
|
|
37
|
+
name: 'Bulk Publishing AI',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
}, {
|
|
40
|
+
capabilities: {
|
|
41
|
+
tools: {},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
// ============================================
|
|
45
|
+
// REGISTER ALL TOOL GROUPS
|
|
46
|
+
// ============================================
|
|
47
|
+
registerGenerationTools(server);
|
|
48
|
+
registerResearchTools(server);
|
|
49
|
+
registerWordPressTools(server);
|
|
50
|
+
registerIndexingTools(server);
|
|
51
|
+
registerSEOTools(server);
|
|
52
|
+
registerProjectTools(server);
|
|
53
|
+
registerCSVTools(server);
|
|
54
|
+
// ============================================
|
|
55
|
+
// START SERVER
|
|
56
|
+
// ============================================
|
|
57
|
+
async function main() {
|
|
58
|
+
// Step 1: Authenticate with BPAI account
|
|
59
|
+
try {
|
|
60
|
+
await authenticate();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.error(`[BPAI MCP Server] Authentication failed: ${error.message}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
// Step 2: Connect MCP transport
|
|
67
|
+
const transport = new StdioServerTransport();
|
|
68
|
+
await server.connect(transport);
|
|
69
|
+
// Log to stderr (stdout is reserved for MCP protocol messages)
|
|
70
|
+
console.error('[BPAI MCP Server] Started successfully');
|
|
71
|
+
console.error('[BPAI MCP Server] Available tool groups: generation, research, wordpress, indexing, seo, projects, csv');
|
|
72
|
+
console.error('[BPAI MCP Server] Transport: stdio');
|
|
73
|
+
}
|
|
74
|
+
main().catch((error) => {
|
|
75
|
+
console.error('[BPAI MCP Server] Fatal error:', error);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO;AACP,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,qBAAqB;AACrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,+CAA+C;AAC/C,eAAe;AACf,+CAA+C;AAE/C,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IACzB,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,OAAO;CACnB,EAAE;IACC,YAAY,EAAE;QACV,KAAK,EAAE,EAAE;KACZ;CACJ,CAAC,CAAC;AAEH,+CAA+C;AAC/C,2BAA2B;AAC3B,+CAA+C;AAE/C,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAEzB,+CAA+C;AAC/C,eAAe;AACf,+CAA+C;AAE/C,KAAK,UAAU,IAAI;IACf,yCAAyC;IACzC,IAAI,CAAC;QACD,MAAM,YAAY,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,4CAA4C,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,gCAAgC;IAChC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,+DAA+D;IAC/D,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACxD,OAAO,CAAC,KAAK,CAAC,wGAAwG,CAAC,CAAC;IACxH,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;AACxD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BPAI MCP Server — CSV Import / Export Tools
|
|
3
|
+
*
|
|
4
|
+
* MCP tool handlers for:
|
|
5
|
+
* - import_csv: Read a local CSV and auto-detect columns
|
|
6
|
+
* - export_csv: Write articles to a CSV file
|
|
7
|
+
* - export_markdown: Write articles as individual .md files
|
|
8
|
+
* - export_json: Write articles as structured JSON
|
|
9
|
+
* - export_bulk: Combined multi-format export with filtering
|
|
10
|
+
*
|
|
11
|
+
* Security:
|
|
12
|
+
* - All operations run on the user's local machine (filesystem only)
|
|
13
|
+
* - No database or remote calls; articles are provided inline by the MCP client
|
|
14
|
+
* - Auth check on every tool call via getUserContext()
|
|
15
|
+
*/
|
|
16
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
17
|
+
export declare function registerCSVTools(server: McpServer): void;
|
|
18
|
+
//# sourceMappingURL=csv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/tools/csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA6QzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,QAsbjD"}
|