@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.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/build/common/apiClient.d.ts +14 -0
  4. package/build/common/apiClient.d.ts.map +1 -0
  5. package/build/common/apiClient.js +68 -0
  6. package/build/common/personaClient.d.ts +48 -0
  7. package/build/common/personaClient.d.ts.map +1 -0
  8. package/build/common/personaClient.js +101 -0
  9. package/build/common/promptBuilder.d.ts +10 -0
  10. package/build/common/promptBuilder.d.ts.map +1 -0
  11. package/build/common/promptBuilder.js +14 -0
  12. package/build/common/providerConfig.d.ts +23 -0
  13. package/build/common/providerConfig.d.ts.map +1 -0
  14. package/build/common/providerConfig.js +43 -0
  15. package/build/common/tokenFormatter.d.ts +12 -0
  16. package/build/common/tokenFormatter.d.ts.map +1 -0
  17. package/build/common/tokenFormatter.js +24 -0
  18. package/build/core/CommandDispatcher.d.ts +81 -0
  19. package/build/core/CommandDispatcher.d.ts.map +1 -0
  20. package/build/core/CommandDispatcher.js +242 -0
  21. package/build/core/ToolCommand.d.ts +188 -0
  22. package/build/core/ToolCommand.d.ts.map +1 -0
  23. package/build/core/ToolCommand.js +63 -0
  24. package/build/core/ToolRegistry.d.ts +80 -0
  25. package/build/core/ToolRegistry.d.ts.map +1 -0
  26. package/build/core/ToolRegistry.js +279 -0
  27. package/build/index.d.ts +8 -0
  28. package/build/index.d.ts.map +1 -0
  29. package/build/index.js +329 -0
  30. package/build/infra/eventBus.d.ts +120 -0
  31. package/build/infra/eventBus.d.ts.map +1 -0
  32. package/build/infra/eventBus.js +138 -0
  33. package/build/personas/ada/index.d.ts +15 -0
  34. package/build/personas/ada/index.d.ts.map +1 -0
  35. package/build/personas/ada/index.js +121 -0
  36. package/build/personas/atlas/index.d.ts +13 -0
  37. package/build/personas/atlas/index.d.ts.map +1 -0
  38. package/build/personas/atlas/index.js +65 -0
  39. package/build/personas/charles/index.d.ts +18 -0
  40. package/build/personas/charles/index.d.ts.map +1 -0
  41. package/build/personas/charles/index.js +190 -0
  42. package/build/personas/hermes/index.d.ts +13 -0
  43. package/build/personas/hermes/index.d.ts.map +1 -0
  44. package/build/personas/hermes/index.js +61 -0
  45. package/build/personas/iris/index.d.ts +13 -0
  46. package/build/personas/iris/index.d.ts.map +1 -0
  47. package/build/personas/iris/index.js +61 -0
  48. package/build/personas/router.d.ts +18 -0
  49. package/build/personas/router.d.ts.map +1 -0
  50. package/build/personas/router.js +302 -0
  51. package/build/personas/sentinel/index.d.ts +13 -0
  52. package/build/personas/sentinel/index.d.ts.map +1 -0
  53. package/build/personas/sentinel/index.js +62 -0
  54. package/build/personas/types.d.ts +91 -0
  55. package/build/personas/types.d.ts.map +1 -0
  56. package/build/personas/types.js +60 -0
  57. package/build/personas/xavier/index.d.ts +14 -0
  58. package/build/personas/xavier/index.d.ts.map +1 -0
  59. package/build/personas/xavier/index.js +80 -0
  60. package/build/prompts/architectPrompts.d.ts +5 -0
  61. package/build/prompts/architectPrompts.d.ts.map +1 -0
  62. package/build/prompts/architectPrompts.js +58 -0
  63. package/build/prompts/codeadvicePrompts.d.ts +5 -0
  64. package/build/prompts/codeadvicePrompts.d.ts.map +1 -0
  65. package/build/prompts/codeadvicePrompts.js +13 -0
  66. package/build/prompts/researcherPrompts.d.ts +2 -0
  67. package/build/prompts/researcherPrompts.d.ts.map +1 -0
  68. package/build/prompts/researcherPrompts.js +39 -0
  69. package/build/tools/architect.d.ts +32 -0
  70. package/build/tools/architect.d.ts.map +1 -0
  71. package/build/tools/architect.js +75 -0
  72. package/build/tools/ask.d.ts +39 -0
  73. package/build/tools/ask.d.ts.map +1 -0
  74. package/build/tools/ask.js +139 -0
  75. package/build/tools/codeReview.d.ts +22 -0
  76. package/build/tools/codeReview.d.ts.map +1 -0
  77. package/build/tools/codeReview.js +35 -0
  78. package/build/tools/codeadvice.d.ts +29 -0
  79. package/build/tools/codeadvice.d.ts.map +1 -0
  80. package/build/tools/codeadvice.js +56 -0
  81. package/build/tools/discover.d.ts +24 -0
  82. package/build/tools/discover.d.ts.map +1 -0
  83. package/build/tools/discover.js +220 -0
  84. package/build/tools/persona.d.ts +48 -0
  85. package/build/tools/persona.d.ts.map +1 -0
  86. package/build/tools/persona.js +108 -0
  87. package/build/tools/researcher.d.ts +61 -0
  88. package/build/tools/researcher.d.ts.map +1 -0
  89. package/build/tools/researcher.js +346 -0
  90. package/build/tools/screenshot.d.ts +28 -0
  91. package/build/tools/screenshot.d.ts.map +1 -0
  92. package/build/tools/screenshot.js +46 -0
  93. 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
+ }