@identikey/coding-mcp 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/build/common/apiClient.d.ts +14 -0
- package/build/common/apiClient.d.ts.map +1 -0
- package/build/common/apiClient.js +68 -0
- package/build/common/personaClient.d.ts +48 -0
- package/build/common/personaClient.d.ts.map +1 -0
- package/build/common/personaClient.js +101 -0
- package/build/common/promptBuilder.d.ts +10 -0
- package/build/common/promptBuilder.d.ts.map +1 -0
- package/build/common/promptBuilder.js +14 -0
- package/build/common/providerConfig.d.ts +23 -0
- package/build/common/providerConfig.d.ts.map +1 -0
- package/build/common/providerConfig.js +43 -0
- package/build/common/tokenFormatter.d.ts +12 -0
- package/build/common/tokenFormatter.d.ts.map +1 -0
- package/build/common/tokenFormatter.js +24 -0
- package/build/core/CommandDispatcher.d.ts +81 -0
- package/build/core/CommandDispatcher.d.ts.map +1 -0
- package/build/core/CommandDispatcher.js +242 -0
- package/build/core/ToolCommand.d.ts +188 -0
- package/build/core/ToolCommand.d.ts.map +1 -0
- package/build/core/ToolCommand.js +63 -0
- package/build/core/ToolRegistry.d.ts +80 -0
- package/build/core/ToolRegistry.d.ts.map +1 -0
- package/build/core/ToolRegistry.js +279 -0
- package/build/index.d.ts +8 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +329 -0
- package/build/infra/eventBus.d.ts +120 -0
- package/build/infra/eventBus.d.ts.map +1 -0
- package/build/infra/eventBus.js +138 -0
- package/build/personas/ada/index.d.ts +15 -0
- package/build/personas/ada/index.d.ts.map +1 -0
- package/build/personas/ada/index.js +121 -0
- package/build/personas/atlas/index.d.ts +13 -0
- package/build/personas/atlas/index.d.ts.map +1 -0
- package/build/personas/atlas/index.js +65 -0
- package/build/personas/charles/index.d.ts +18 -0
- package/build/personas/charles/index.d.ts.map +1 -0
- package/build/personas/charles/index.js +190 -0
- package/build/personas/hermes/index.d.ts +13 -0
- package/build/personas/hermes/index.d.ts.map +1 -0
- package/build/personas/hermes/index.js +61 -0
- package/build/personas/iris/index.d.ts +13 -0
- package/build/personas/iris/index.d.ts.map +1 -0
- package/build/personas/iris/index.js +61 -0
- package/build/personas/router.d.ts +18 -0
- package/build/personas/router.d.ts.map +1 -0
- package/build/personas/router.js +302 -0
- package/build/personas/sentinel/index.d.ts +13 -0
- package/build/personas/sentinel/index.d.ts.map +1 -0
- package/build/personas/sentinel/index.js +62 -0
- package/build/personas/types.d.ts +91 -0
- package/build/personas/types.d.ts.map +1 -0
- package/build/personas/types.js +60 -0
- package/build/personas/xavier/index.d.ts +14 -0
- package/build/personas/xavier/index.d.ts.map +1 -0
- package/build/personas/xavier/index.js +80 -0
- package/build/prompts/architectPrompts.d.ts +5 -0
- package/build/prompts/architectPrompts.d.ts.map +1 -0
- package/build/prompts/architectPrompts.js +58 -0
- package/build/prompts/codeadvicePrompts.d.ts +5 -0
- package/build/prompts/codeadvicePrompts.d.ts.map +1 -0
- package/build/prompts/codeadvicePrompts.js +13 -0
- package/build/prompts/researcherPrompts.d.ts +2 -0
- package/build/prompts/researcherPrompts.d.ts.map +1 -0
- package/build/prompts/researcherPrompts.js +39 -0
- package/build/tools/architect.d.ts +32 -0
- package/build/tools/architect.d.ts.map +1 -0
- package/build/tools/architect.js +75 -0
- package/build/tools/ask.d.ts +39 -0
- package/build/tools/ask.d.ts.map +1 -0
- package/build/tools/ask.js +139 -0
- package/build/tools/codeReview.d.ts +22 -0
- package/build/tools/codeReview.d.ts.map +1 -0
- package/build/tools/codeReview.js +35 -0
- package/build/tools/codeadvice.d.ts +29 -0
- package/build/tools/codeadvice.d.ts.map +1 -0
- package/build/tools/codeadvice.js +56 -0
- package/build/tools/discover.d.ts +24 -0
- package/build/tools/discover.d.ts.map +1 -0
- package/build/tools/discover.js +220 -0
- package/build/tools/persona.d.ts +48 -0
- package/build/tools/persona.d.ts.map +1 -0
- package/build/tools/persona.js +108 -0
- package/build/tools/researcher.d.ts +61 -0
- package/build/tools/researcher.d.ts.map +1 -0
- package/build/tools/researcher.js +346 -0
- package/build/tools/screenshot.d.ts +28 -0
- package/build/tools/screenshot.d.ts.map +1 -0
- package/build/tools/screenshot.js +46 -0
- package/package.json +56 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { callAIWithPersona } from "../common/personaClient.js";
|
|
3
|
+
import { PersonaRegistry } from "../personas/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Persona tool - Direct interaction with registered personas
|
|
6
|
+
* Allows calling any registered persona for advice, analysis, or conversation
|
|
7
|
+
*/
|
|
8
|
+
export const personaToolName = "persona";
|
|
9
|
+
export const personaToolDescription = "Chat with a specific persona for specialized advice and analysis with personality";
|
|
10
|
+
export const PersonaToolSchema = z.object({
|
|
11
|
+
persona_id: z
|
|
12
|
+
.string()
|
|
13
|
+
.describe("ID of the persona to interact with (e.g., 'charles')"),
|
|
14
|
+
query: z
|
|
15
|
+
.string()
|
|
16
|
+
.min(1, "Query is required")
|
|
17
|
+
.describe("Question or request for the persona"),
|
|
18
|
+
context: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Additional context or code for the persona to analyze"),
|
|
22
|
+
analysis_type: z
|
|
23
|
+
.enum(["comprehensive", "advice", "research", "review"])
|
|
24
|
+
.optional()
|
|
25
|
+
.default("advice")
|
|
26
|
+
.describe("Type of analysis needed"),
|
|
27
|
+
reasoning_effort: z
|
|
28
|
+
.enum(["low", "medium", "high"])
|
|
29
|
+
.optional()
|
|
30
|
+
.default("medium")
|
|
31
|
+
.describe("Reasoning effort level"),
|
|
32
|
+
tone_style: z
|
|
33
|
+
.enum(["concise", "detailed", "humorous", "straight"])
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Response tone (concise=direct, detailed=balanced, humorous=witty, straight=professional)"),
|
|
36
|
+
output_format: z
|
|
37
|
+
.enum(["tldr", "detailed", "dual"])
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Output format (tldr=brief summary, detailed=full analysis, dual=both)"),
|
|
40
|
+
audience_level: z
|
|
41
|
+
.enum(["beginner", "intermediate", "expert", "auto"])
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Target audience expertise level"),
|
|
44
|
+
include_diagrams: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Include visual diagrams where applicable"),
|
|
48
|
+
user_constraints: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("User's constraints or context (e.g., 'tight deadline', 'legacy codebase')"),
|
|
52
|
+
});
|
|
53
|
+
export async function runPersonaTool(args) {
|
|
54
|
+
const { persona_id, query, context = "", analysis_type, reasoning_effort, tone_style, output_format, audience_level, include_diagrams, user_constraints, } = args;
|
|
55
|
+
// Verify persona exists
|
|
56
|
+
const persona = PersonaRegistry.get(persona_id);
|
|
57
|
+
if (!persona) {
|
|
58
|
+
const available = PersonaRegistry.list()
|
|
59
|
+
.map((p) => p.id)
|
|
60
|
+
.join(", ");
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: `Persona '${persona_id}' not found. Available personas: ${available || "none registered"}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
// Build a simple system prompt for persona interaction
|
|
72
|
+
const systemPrompt = `You are having a conversation with the user, providing your expertise and guidance.
|
|
73
|
+
|
|
74
|
+
Your role is to be helpful while maintaining your distinct personality and expertise.
|
|
75
|
+
Respond naturally and directly to their query.`;
|
|
76
|
+
const result = await callAIWithPersona({
|
|
77
|
+
systemPrompt,
|
|
78
|
+
task: query,
|
|
79
|
+
code: context,
|
|
80
|
+
analysisType: analysis_type,
|
|
81
|
+
reasoningEffort: reasoning_effort,
|
|
82
|
+
personaId: persona_id,
|
|
83
|
+
toneStyle: tone_style,
|
|
84
|
+
outputFormat: output_format,
|
|
85
|
+
audienceLevel: audience_level,
|
|
86
|
+
includeDiagrams: include_diagrams,
|
|
87
|
+
userConstraints: user_constraints,
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: result,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: `Error calling ${persona.name}: ${error.message || error}`,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Researcher tool
|
|
4
|
+
* - Performs multi-source research using various search engines and APIs
|
|
5
|
+
* - Manages sources with proper citation formatting
|
|
6
|
+
* - Provides comprehensive research synthesis with source attribution
|
|
7
|
+
*/
|
|
8
|
+
export declare const researcherToolName = "researcher";
|
|
9
|
+
export declare const researcherToolDescription = "Conducts comprehensive research using multiple sources, manages citations, and synthesizes information with proper source attribution.";
|
|
10
|
+
export interface Source {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
url: string;
|
|
14
|
+
snippet: string;
|
|
15
|
+
searchEngine: string;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
relevanceScore?: number;
|
|
18
|
+
authors?: string[];
|
|
19
|
+
publishDate?: string;
|
|
20
|
+
domain?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ResearchResult {
|
|
23
|
+
query: string;
|
|
24
|
+
sources: Source[];
|
|
25
|
+
synthesis: string;
|
|
26
|
+
citations: Map<string, Source>;
|
|
27
|
+
searchEnginesUsed: string[];
|
|
28
|
+
timestamp: string;
|
|
29
|
+
}
|
|
30
|
+
export declare const ResearcherToolSchema: z.ZodObject<{
|
|
31
|
+
query: z.ZodString;
|
|
32
|
+
search_engines: z.ZodOptional<z.ZodArray<z.ZodEnum<["google", "xai", "arxiv", "wikipedia", "github", "stackexchange", "pubmed", "semantic_scholar"]>, "many">>;
|
|
33
|
+
max_results_per_engine: z.ZodOptional<z.ZodNumber>;
|
|
34
|
+
deep_search: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
include_academic: z.ZodOptional<z.ZodBoolean>;
|
|
36
|
+
reasoning_effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high"]>>;
|
|
37
|
+
citation_style: z.ZodOptional<z.ZodEnum<["apa", "mla", "chicago", "ieee", "inline"]>>;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
query: string;
|
|
40
|
+
reasoning_effort?: "low" | "medium" | "high" | undefined;
|
|
41
|
+
search_engines?: ("xai" | "google" | "arxiv" | "wikipedia" | "github" | "stackexchange" | "pubmed" | "semantic_scholar")[] | undefined;
|
|
42
|
+
max_results_per_engine?: number | undefined;
|
|
43
|
+
deep_search?: boolean | undefined;
|
|
44
|
+
include_academic?: boolean | undefined;
|
|
45
|
+
citation_style?: "apa" | "mla" | "chicago" | "ieee" | "inline" | undefined;
|
|
46
|
+
}, {
|
|
47
|
+
query: string;
|
|
48
|
+
reasoning_effort?: "low" | "medium" | "high" | undefined;
|
|
49
|
+
search_engines?: ("xai" | "google" | "arxiv" | "wikipedia" | "github" | "stackexchange" | "pubmed" | "semantic_scholar")[] | undefined;
|
|
50
|
+
max_results_per_engine?: number | undefined;
|
|
51
|
+
deep_search?: boolean | undefined;
|
|
52
|
+
include_academic?: boolean | undefined;
|
|
53
|
+
citation_style?: "apa" | "mla" | "chicago" | "ieee" | "inline" | undefined;
|
|
54
|
+
}>;
|
|
55
|
+
export declare function runResearcherTool(args: z.infer<typeof ResearcherToolSchema>): Promise<{
|
|
56
|
+
content: {
|
|
57
|
+
type: string;
|
|
58
|
+
text: string;
|
|
59
|
+
}[];
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=researcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"researcher.d.ts","sourceRoot":"","sources":["../../src/tools/researcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;GAKG;AAEH,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAC/C,eAAO,MAAM,yBAAyB,2IACoG,CAAC;AAG3I,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;EA+C/B,CAAC;AAqPH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC;;;;;GAiI3C"}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { callAIProvider } from "../common/apiClient.js";
|
|
3
|
+
import { chooseProvider, } from "../common/providerConfig.js";
|
|
4
|
+
import { RESEARCHER_SYSTEM_PROMPT } from "../prompts/researcherPrompts.js";
|
|
5
|
+
/**
|
|
6
|
+
* Researcher tool
|
|
7
|
+
* - Performs multi-source research using various search engines and APIs
|
|
8
|
+
* - Manages sources with proper citation formatting
|
|
9
|
+
* - Provides comprehensive research synthesis with source attribution
|
|
10
|
+
*/
|
|
11
|
+
export const researcherToolName = "researcher";
|
|
12
|
+
export const researcherToolDescription = "Conducts comprehensive research using multiple sources, manages citations, and synthesizes information with proper source attribution.";
|
|
13
|
+
export const ResearcherToolSchema = z.object({
|
|
14
|
+
query: z.string().min(1, "Research query is required."),
|
|
15
|
+
search_engines: z
|
|
16
|
+
.array(z.enum([
|
|
17
|
+
"google",
|
|
18
|
+
"xai",
|
|
19
|
+
"arxiv",
|
|
20
|
+
"wikipedia",
|
|
21
|
+
"github",
|
|
22
|
+
"stackexchange",
|
|
23
|
+
"pubmed",
|
|
24
|
+
"semantic_scholar",
|
|
25
|
+
]))
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Search engines to use. Defaults to ['google', 'xai']. Available: google, xai, arxiv, wikipedia, github, stackexchange, pubmed, semantic_scholar"),
|
|
28
|
+
max_results_per_engine: z
|
|
29
|
+
.number()
|
|
30
|
+
.min(1)
|
|
31
|
+
.max(20)
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Maximum results per search engine (1-20). Defaults to 5."),
|
|
34
|
+
deep_search: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Whether to perform deep search by following links and extracting full content. Defaults to false."),
|
|
38
|
+
include_academic: z
|
|
39
|
+
.boolean()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Whether to prioritize academic sources (arxiv, pubmed, semantic_scholar). Defaults to false."),
|
|
42
|
+
reasoning_effort: z
|
|
43
|
+
.enum(["low", "medium", "high"])
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("How hard the AI should think when synthesizing results (low/medium/high). Defaults to high for research."),
|
|
46
|
+
citation_style: z
|
|
47
|
+
.enum(["apa", "mla", "chicago", "ieee", "inline"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Citation style to use. Defaults to 'inline' for easy reading."),
|
|
50
|
+
});
|
|
51
|
+
// Search API implementations
|
|
52
|
+
async function searchGoogle(query, maxResults) {
|
|
53
|
+
// Google Custom Search API implementation
|
|
54
|
+
// Note: Requires GOOGLE_API_KEY and GOOGLE_CSE_ID environment variables
|
|
55
|
+
const apiKey = process.env.GOOGLE_API_KEY;
|
|
56
|
+
const cseId = process.env.GOOGLE_CSE_ID;
|
|
57
|
+
if (!apiKey || !cseId) {
|
|
58
|
+
console.warn("Google Search API credentials not configured");
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const url = `https://www.googleapis.com/customsearch/v1?key=${apiKey}&cx=${cseId}&q=${encodeURIComponent(query)}&num=${maxResults}`;
|
|
63
|
+
const response = await fetch(url);
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
return (data.items || []).map((item, index) => ({
|
|
66
|
+
id: `google-${index}`,
|
|
67
|
+
title: item.title,
|
|
68
|
+
url: item.link,
|
|
69
|
+
snippet: item.snippet || "",
|
|
70
|
+
searchEngine: "Google",
|
|
71
|
+
timestamp: new Date().toISOString(),
|
|
72
|
+
domain: new URL(item.link).hostname,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error("Google search error:", error);
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function searchXAI(query, maxResults) {
|
|
81
|
+
// X.AI/Perplexity API implementation
|
|
82
|
+
// This would integrate with Perplexity or X.AI's search capabilities
|
|
83
|
+
const apiKey = process.env.XAI_API_KEY || process.env.PERPLEXITY_API_KEY;
|
|
84
|
+
if (!apiKey) {
|
|
85
|
+
console.warn("X.AI/Perplexity API credentials not configured");
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
// Perplexity API example
|
|
90
|
+
const response = await fetch("https://api.perplexity.ai/search", {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: {
|
|
93
|
+
Authorization: `Bearer ${apiKey}`,
|
|
94
|
+
"Content-Type": "application/json",
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
query,
|
|
98
|
+
max_results: maxResults,
|
|
99
|
+
search_depth: "comprehensive",
|
|
100
|
+
include_sources: true,
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
const data = await response.json();
|
|
104
|
+
return (data.sources || []).map((source, index) => ({
|
|
105
|
+
id: `xai-${index}`,
|
|
106
|
+
title: source.title,
|
|
107
|
+
url: source.url,
|
|
108
|
+
snippet: source.excerpt || "",
|
|
109
|
+
searchEngine: "X.AI/Perplexity",
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
domain: new URL(source.url).hostname,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error("X.AI search error:", error);
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function searchArxiv(query, maxResults) {
|
|
120
|
+
// ArXiv API implementation for academic papers
|
|
121
|
+
try {
|
|
122
|
+
const url = `http://export.arxiv.org/api/query?search_query=all:${encodeURIComponent(query)}&start=0&max_results=${maxResults}`;
|
|
123
|
+
const response = await fetch(url);
|
|
124
|
+
const text = await response.text();
|
|
125
|
+
// Simple XML parsing (in production, use a proper XML parser)
|
|
126
|
+
const entries = text.match(/<entry>[\s\S]*?<\/entry>/g) || [];
|
|
127
|
+
return entries.map((entry, index) => {
|
|
128
|
+
const title = (entry.match(/<title>([\s\S]*?)<\/title>/) || [])[1]?.trim() || "";
|
|
129
|
+
const summary = (entry.match(/<summary>([\s\S]*?)<\/summary>/) || [])[1]?.trim() || "";
|
|
130
|
+
const id = (entry.match(/<id>([\s\S]*?)<\/id>/) || [])[1]?.trim() || "";
|
|
131
|
+
const authors = (entry.match(/<author>[\s\S]*?<name>([\s\S]*?)<\/name>[\s\S]*?<\/author>/g) || [])
|
|
132
|
+
.map((a) => (a.match(/<name>([\s\S]*?)<\/name>/) || [])[1]?.trim())
|
|
133
|
+
.filter(Boolean);
|
|
134
|
+
return {
|
|
135
|
+
id: `arxiv-${index}`,
|
|
136
|
+
title,
|
|
137
|
+
url: id,
|
|
138
|
+
snippet: summary.substring(0, 200) + "...",
|
|
139
|
+
searchEngine: "ArXiv",
|
|
140
|
+
timestamp: new Date().toISOString(),
|
|
141
|
+
authors,
|
|
142
|
+
domain: "arxiv.org",
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.error("ArXiv search error:", error);
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function searchWikipedia(query, maxResults) {
|
|
152
|
+
// Wikipedia API implementation
|
|
153
|
+
try {
|
|
154
|
+
const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(query)}&format=json&srlimit=${maxResults}`;
|
|
155
|
+
const response = await fetch(url);
|
|
156
|
+
const data = await response.json();
|
|
157
|
+
return (data.query?.search || []).map((item, index) => ({
|
|
158
|
+
id: `wiki-${index}`,
|
|
159
|
+
title: item.title,
|
|
160
|
+
url: `https://en.wikipedia.org/wiki/${encodeURIComponent(item.title.replace(/ /g, "_"))}`,
|
|
161
|
+
snippet: item.snippet.replace(/<[^>]*>/g, ""), // Remove HTML tags
|
|
162
|
+
searchEngine: "Wikipedia",
|
|
163
|
+
timestamp: new Date().toISOString(),
|
|
164
|
+
domain: "wikipedia.org",
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error("Wikipedia search error:", error);
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function searchGitHub(query, maxResults) {
|
|
173
|
+
// GitHub API implementation for code/repository search
|
|
174
|
+
const token = process.env.GITHUB_TOKEN;
|
|
175
|
+
try {
|
|
176
|
+
const headers = { Accept: "application/vnd.github.v3+json" };
|
|
177
|
+
if (token)
|
|
178
|
+
headers["Authorization"] = `token ${token}`;
|
|
179
|
+
const url = `https://api.github.com/search/repositories?q=${encodeURIComponent(query)}&sort=stars&order=desc&per_page=${maxResults}`;
|
|
180
|
+
const response = await fetch(url, { headers });
|
|
181
|
+
const data = await response.json();
|
|
182
|
+
return (data.items || []).map((repo, index) => ({
|
|
183
|
+
id: `github-${index}`,
|
|
184
|
+
title: repo.full_name,
|
|
185
|
+
url: repo.html_url,
|
|
186
|
+
snippet: repo.description || "No description available",
|
|
187
|
+
searchEngine: "GitHub",
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
domain: "github.com",
|
|
190
|
+
relevanceScore: repo.stargazers_count,
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
console.error("GitHub search error:", error);
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async function searchStackExchange(query, maxResults) {
|
|
199
|
+
// StackExchange API implementation
|
|
200
|
+
try {
|
|
201
|
+
const url = `https://api.stackexchange.com/2.3/search/advanced?order=desc&sort=relevance&q=${encodeURIComponent(query)}&site=stackoverflow&pagesize=${maxResults}`;
|
|
202
|
+
const response = await fetch(url);
|
|
203
|
+
const data = await response.json();
|
|
204
|
+
return (data.items || []).map((item, index) => ({
|
|
205
|
+
id: `stack-${index}`,
|
|
206
|
+
title: item.title,
|
|
207
|
+
url: item.link,
|
|
208
|
+
snippet: `Score: ${item.score}, Answers: ${item.answer_count}`,
|
|
209
|
+
searchEngine: "StackOverflow",
|
|
210
|
+
timestamp: new Date().toISOString(),
|
|
211
|
+
domain: "stackoverflow.com",
|
|
212
|
+
relevanceScore: item.score,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.error("StackExchange search error:", error);
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Citation formatter
|
|
221
|
+
function formatCitation(source, style) {
|
|
222
|
+
const date = new Date(source.timestamp).toLocaleDateString();
|
|
223
|
+
switch (style) {
|
|
224
|
+
case "apa":
|
|
225
|
+
return `${source.authors?.join(", ") || source.domain}. (${date}). ${source.title}. Retrieved from ${source.url}`;
|
|
226
|
+
case "mla":
|
|
227
|
+
return `${source.authors?.join(", ") || source.domain}. "${source.title}." Web. ${date}. <${source.url}>`;
|
|
228
|
+
case "chicago":
|
|
229
|
+
return `${source.authors?.join(", ") || source.domain}. "${source.title}." Accessed ${date}. ${source.url}.`;
|
|
230
|
+
case "ieee":
|
|
231
|
+
return `[${source.id}] ${source.authors?.join(", ") || source.domain}, "${source.title}," ${source.searchEngine}, ${date}. [Online]. Available: ${source.url}`;
|
|
232
|
+
case "inline":
|
|
233
|
+
default:
|
|
234
|
+
return `[${source.title}](${source.url}) - ${source.searchEngine}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Main research function
|
|
238
|
+
export async function runResearcherTool(args) {
|
|
239
|
+
const { query, search_engines = ["google", "xai"], max_results_per_engine = 5, deep_search = false, include_academic = false, reasoning_effort = "high", citation_style = "inline", } = args;
|
|
240
|
+
try {
|
|
241
|
+
// Expand search engines if academic sources requested
|
|
242
|
+
let engines = [...search_engines];
|
|
243
|
+
if (include_academic && !engines.includes("arxiv")) {
|
|
244
|
+
engines.push("arxiv");
|
|
245
|
+
}
|
|
246
|
+
if (include_academic && !engines.includes("semantic_scholar")) {
|
|
247
|
+
engines.push("semantic_scholar");
|
|
248
|
+
}
|
|
249
|
+
// Perform searches in parallel
|
|
250
|
+
const searchPromises = engines.map(async (engine) => {
|
|
251
|
+
switch (engine) {
|
|
252
|
+
case "google":
|
|
253
|
+
return await searchGoogle(query, max_results_per_engine);
|
|
254
|
+
case "xai":
|
|
255
|
+
return await searchXAI(query, max_results_per_engine);
|
|
256
|
+
case "arxiv":
|
|
257
|
+
return await searchArxiv(query, max_results_per_engine);
|
|
258
|
+
case "wikipedia":
|
|
259
|
+
return await searchWikipedia(query, max_results_per_engine);
|
|
260
|
+
case "github":
|
|
261
|
+
return await searchGitHub(query, max_results_per_engine);
|
|
262
|
+
case "stackexchange":
|
|
263
|
+
return await searchStackExchange(query, max_results_per_engine);
|
|
264
|
+
default:
|
|
265
|
+
return [];
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
const searchResults = await Promise.all(searchPromises);
|
|
269
|
+
const allSources = searchResults.flat();
|
|
270
|
+
// Create citations map
|
|
271
|
+
const citations = new Map();
|
|
272
|
+
allSources.forEach((source) => {
|
|
273
|
+
citations.set(source.id, source);
|
|
274
|
+
});
|
|
275
|
+
// Prepare source content for AI synthesis
|
|
276
|
+
const sourcesContent = allSources
|
|
277
|
+
.map((source) => `[${source.id}] ${source.title}\n${source.searchEngine} - ${source.url}\n${source.snippet}\n`)
|
|
278
|
+
.join("\n---\n");
|
|
279
|
+
// Call AI provider for synthesis
|
|
280
|
+
const selectedProvider = chooseProvider({
|
|
281
|
+
analysisType: "research",
|
|
282
|
+
reasoningEffort: reasoning_effort,
|
|
283
|
+
textHint: query,
|
|
284
|
+
});
|
|
285
|
+
const synthesisPrompt = `Research Query: ${query}
|
|
286
|
+
|
|
287
|
+
Sources Found:
|
|
288
|
+
${sourcesContent}
|
|
289
|
+
|
|
290
|
+
Please synthesize these sources into a comprehensive research summary. Include:
|
|
291
|
+
1. Key findings and insights
|
|
292
|
+
2. Conflicting information or debates
|
|
293
|
+
3. Knowledge gaps or areas needing more research
|
|
294
|
+
4. Proper source attribution using [source-id] references
|
|
295
|
+
|
|
296
|
+
Citation Style: ${citation_style}`;
|
|
297
|
+
const synthesis = await callAIProvider({
|
|
298
|
+
systemPrompt: RESEARCHER_SYSTEM_PROMPT,
|
|
299
|
+
task: synthesisPrompt,
|
|
300
|
+
code: "", // Not needed for research
|
|
301
|
+
analysisType: "research",
|
|
302
|
+
reasoningEffort: reasoning_effort,
|
|
303
|
+
provider: selectedProvider,
|
|
304
|
+
});
|
|
305
|
+
// Format citations
|
|
306
|
+
const formattedCitations = allSources
|
|
307
|
+
.map((source) => formatCitation(source, citation_style))
|
|
308
|
+
.join("\n");
|
|
309
|
+
// Compile final result
|
|
310
|
+
const result = `# Research Results for: "${query}"
|
|
311
|
+
|
|
312
|
+
## Summary
|
|
313
|
+
${synthesis}
|
|
314
|
+
|
|
315
|
+
## Sources Used (${allSources.length} total)
|
|
316
|
+
${formattedCitations}
|
|
317
|
+
|
|
318
|
+
## Search Engines Consulted
|
|
319
|
+
${engines.join(", ")}
|
|
320
|
+
|
|
321
|
+
## Metadata
|
|
322
|
+
- Timestamp: ${new Date().toISOString()}
|
|
323
|
+
- Deep Search: ${deep_search}
|
|
324
|
+
- Academic Priority: ${include_academic}
|
|
325
|
+
- Citation Style: ${citation_style}
|
|
326
|
+
`;
|
|
327
|
+
return {
|
|
328
|
+
content: [
|
|
329
|
+
{
|
|
330
|
+
type: "text",
|
|
331
|
+
text: result,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
return {
|
|
338
|
+
content: [
|
|
339
|
+
{
|
|
340
|
+
type: "text",
|
|
341
|
+
text: `Research Error: ${error.message || error}`,
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Screenshot tool
|
|
4
|
+
* - Takes in either "url" (a full URL) or "relativePath" to open on localhost:3000
|
|
5
|
+
* - Returns a base64-encoded PNG screenshot
|
|
6
|
+
*/
|
|
7
|
+
export declare const screenshotToolName = "screenshot";
|
|
8
|
+
export declare const screenshotToolDescription = "Take a screenshot of a URL or a local path (relative URL appended to http://localhost:3000).";
|
|
9
|
+
export declare const ScreenshotToolSchema: z.ZodObject<{
|
|
10
|
+
url: z.ZodOptional<z.ZodString>;
|
|
11
|
+
relativePath: z.ZodOptional<z.ZodString>;
|
|
12
|
+
fullPathToScreenshot: z.ZodString;
|
|
13
|
+
}, "strip", z.ZodTypeAny, {
|
|
14
|
+
fullPathToScreenshot: string;
|
|
15
|
+
url?: string | undefined;
|
|
16
|
+
relativePath?: string | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
fullPathToScreenshot: string;
|
|
19
|
+
url?: string | undefined;
|
|
20
|
+
relativePath?: string | undefined;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function runScreenshotTool(args: z.infer<typeof ScreenshotToolSchema>): Promise<{
|
|
23
|
+
content: {
|
|
24
|
+
type: string;
|
|
25
|
+
text: string;
|
|
26
|
+
}[];
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=screenshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB;;;;GAIG;AAEH,eAAO,MAAM,kBAAkB,eAAe,CAAA;AAC9C,eAAO,MAAM,yBAAyB,iGAC0D,CAAA;AAEhG,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAI/B,CAAA;AAEF,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC;;;;;GA+B3C"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import puppeteer from "puppeteer";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
/**
|
|
6
|
+
* Screenshot tool
|
|
7
|
+
* - Takes in either "url" (a full URL) or "relativePath" to open on localhost:3000
|
|
8
|
+
* - Returns a base64-encoded PNG screenshot
|
|
9
|
+
*/
|
|
10
|
+
export const screenshotToolName = "screenshot";
|
|
11
|
+
export const screenshotToolDescription = "Take a screenshot of a URL or a local path (relative URL appended to http://localhost:3000).";
|
|
12
|
+
export const ScreenshotToolSchema = z.object({
|
|
13
|
+
url: z.string().optional(),
|
|
14
|
+
relativePath: z.string().optional(),
|
|
15
|
+
fullPathToScreenshot: z.string(),
|
|
16
|
+
});
|
|
17
|
+
export async function runScreenshotTool(args) {
|
|
18
|
+
// Determine final URL
|
|
19
|
+
let finalUrl = args.url;
|
|
20
|
+
if (!finalUrl) {
|
|
21
|
+
if (!args.relativePath) {
|
|
22
|
+
throw new Error("Must provide either 'url' or 'relativePath'");
|
|
23
|
+
}
|
|
24
|
+
finalUrl = `http://localhost:3000/${args.relativePath.replace(/^\//, "")}`;
|
|
25
|
+
}
|
|
26
|
+
const fullPathToScreenshot = path.resolve(args.fullPathToScreenshot);
|
|
27
|
+
// Launch Puppeteer
|
|
28
|
+
const browser = await puppeteer.launch();
|
|
29
|
+
const page = await browser.newPage();
|
|
30
|
+
await page.goto(finalUrl);
|
|
31
|
+
const screenshotBuffer = (await page.screenshot({
|
|
32
|
+
fullPage: true,
|
|
33
|
+
}));
|
|
34
|
+
await browser.close();
|
|
35
|
+
await fs.promises.writeFile(fullPathToScreenshot, screenshotBuffer);
|
|
36
|
+
// Return the base64 representation
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: `Screenshot saved to ${fullPathToScreenshot}. Before continuing, you MUST ask the user to drag and drop the screenshot into the chat window.
|
|
42
|
+
The path to the screenshot is ${fullPathToScreenshot}.`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "@identikey/coding-mcp",
|
|
4
|
+
"version": "2.0.1",
|
|
5
|
+
"description": "MCP Server with expert AI personas for code architecture, reviews, screenshots, and research",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./build/index.js",
|
|
8
|
+
"types": "./build/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"build",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
16
|
+
"start": "node build/index.js",
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"cursor-tools": "./build/index.js"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"cursor",
|
|
27
|
+
"ai",
|
|
28
|
+
"code-review",
|
|
29
|
+
"architecture",
|
|
30
|
+
"personas",
|
|
31
|
+
"screenshot",
|
|
32
|
+
"developer-tools"
|
|
33
|
+
],
|
|
34
|
+
"author": "Duke Jones",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/identikey/coding-mcp.git"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@ai-sdk/xai": "^1.2.18",
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.4.1",
|
|
46
|
+
"ai": "^4.3.19",
|
|
47
|
+
"openai": "^4.82.0",
|
|
48
|
+
"puppeteer": "^24.1.1",
|
|
49
|
+
"zod": "^3.24.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/bun": "^1.2.15",
|
|
53
|
+
"@types/node": "^22.13.0",
|
|
54
|
+
"typescript": "^5.7.3"
|
|
55
|
+
}
|
|
56
|
+
}
|