dsh-codebase-chat-mcp 0.2.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 (3) hide show
  1. package/README.md +86 -0
  2. package/index.mjs +193 -0
  3. package/package.json +28 -0
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # MCP server for dsh-codebase-chat
2
+
3
+ Standalone MCP server for `dsh-codebase-chat`. It does **not** require DeepSeek Harness. It scans local projects and calls an LLM API directly.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js >= 20
8
+ - A DeepSeek / OpenAI-compatible API key: `DEEPSEEK_API_KEY` or `OPENAI_API_KEY`
9
+
10
+ ## Installation
11
+
12
+ ### From npm (when published)
13
+
14
+ ```bash
15
+ npm install -g dsh-codebase-chat-mcp
16
+ # or run without installing
17
+ npx dsh-codebase-chat-mcp
18
+ ```
19
+
20
+ ### From source
21
+
22
+ ```bash
23
+ git clone https://github.com/shinzarou-eng/dsh-codebase-chat.git
24
+ cd dsh-codebase-chat/mcp
25
+ pnpm install
26
+ pnpm link ../
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ Set one of:
32
+
33
+ ```powershell
34
+ $env:DEEPSEEK_API_KEY = "sk-..."
35
+ # or
36
+ $env:OPENAI_API_KEY = "sk-..."
37
+ ```
38
+
39
+ Optional:
40
+
41
+ - `DEEPSEEK_BASE_URL` or `OPENAI_BASE_URL` (default: `https://api.deepseek.com/v1`)
42
+ - `CODEBASE_MODEL` (default: `deepseek-chat`)
43
+
44
+ ## Usage in Windsurf / Cursor / Claude
45
+
46
+ Add to your MCP config:
47
+
48
+ ```json
49
+ {
50
+ "mcpServers": {
51
+ "dsh-codebase-chat": {
52
+ "command": "node",
53
+ "args": [
54
+ "C:\\Users\\YOU\\dsh-codebase-chat\\mcp\\index.mjs"
55
+ ],
56
+ "env": {
57
+ "DEEPSEEK_API_KEY": "sk-..."
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Tools exposed
65
+
66
+ - `codebase_chat` — Q&A on a local project
67
+ - `codebase_search` — search symbol/term
68
+ - `codebase_explain` — explain a file or symbol
69
+ - `codebase_refactor` — propose a refactor
70
+ - `codebase_intelligence` — full CTO brief
71
+ - `codebase_audit` — tech-debt & non-conformities
72
+ - `codebase_report` — strategic board report
73
+ - `codebase_ceo` — one-page CEO brief
74
+ - `codebase_tasks` — generate a TASKS.md plan
75
+ - `codebase_player` — UX / playthrough brief
76
+ - `codebase_crea` — creative / marketing ideas from the code
77
+
78
+ All tools accept:
79
+
80
+ - `projectPath` (string, absolute or relative path, default: cwd)
81
+ - `lang` (string, `fr` or `en`, default: `fr`)
82
+ - `focus` / `query` (string, optional)
83
+
84
+ ## Transport
85
+
86
+ By default the server uses `stdio` (MCP standard). SSE/HTTP transport can be added in a future version.
package/index.mjs ADDED
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import {
5
+ CallToolRequestSchema,
6
+ ListToolsRequestSchema,
7
+ } from "@modelcontextprotocol/sdk/types.js";
8
+ import {
9
+ collectIntelligenceContext,
10
+ collectAssessmentContext,
11
+ collectNonConformities,
12
+ collectCeoContext,
13
+ collectTasksContext,
14
+ collectCodebaseContext,
15
+ buildIntelligencePrompt,
16
+ buildReportPrompt,
17
+ buildAuditPrompt,
18
+ buildCeoPrompt,
19
+ buildTasksPrompt,
20
+ buildPlayerPrompt,
21
+ buildSearchPrompt,
22
+ buildExplainPrompt,
23
+ buildRefactorPrompt,
24
+ buildChatPrompt,
25
+ buildCreaPrompt,
26
+ resolveProjectPath,
27
+ getProjectName,
28
+ normalizeLabels,
29
+ } from "dsh-codebase-chat";
30
+
31
+ const VERSION = "0.2.0";
32
+
33
+ const apiKey = process.env.DEEPSEEK_API_KEY || process.env.OPENAI_API_KEY || "";
34
+ const baseUrl = process.env.DEEPSEEK_BASE_URL || process.env.OPENAI_BASE_URL || "https://api.deepseek.com/v1";
35
+ const model = process.env.CODEBASE_MODEL || "deepseek-chat";
36
+
37
+ async function callLlm(prompt, lang = "fr") {
38
+ if (!apiKey) {
39
+ throw new Error("Aucune clef API : definissez DEEPSEEK_API_KEY ou OPENAI_API_KEY");
40
+ }
41
+ const res = await fetch(`${baseUrl}/chat/completions`, {
42
+ method: "POST",
43
+ headers: {
44
+ "Content-Type": "application/json",
45
+ Authorization: `Bearer ${apiKey}`,
46
+ },
47
+ body: JSON.stringify({
48
+ model,
49
+ messages: [
50
+ { role: "system", content: lang === "en" ? "You are a senior codebase analyst. Be precise and cite files." : "Tu es un analyste codebase senior. Sois precis et cite les fichiers." },
51
+ { role: "user", content: prompt },
52
+ ],
53
+ temperature: 0.3,
54
+ max_tokens: 8192,
55
+ }),
56
+ });
57
+ if (!res.ok) {
58
+ const text = await res.text().catch(() => "");
59
+ throw new Error(`API error ${res.status}: ${text}`);
60
+ }
61
+ const data = await res.json();
62
+ return data.choices?.[0]?.message?.content || "";
63
+ }
64
+
65
+ function langHint(lang) {
66
+ return lang === "en" ? "Respond strictly in English." : "Reponds obligatoirement en francais.";
67
+ }
68
+
69
+ function getProjectPath(projectPath) {
70
+ return projectPath ? String(projectPath).trim() : process.cwd();
71
+ }
72
+
73
+ function wrapPrompt(prompt, lang) {
74
+ const hint = langHint(lang);
75
+ const final = lang === "en"
76
+ ? "\n\n---\n\nFINAL INSTRUCTION (overrides everything above): The entire response, including section titles, bullet points and conclusion, MUST be written in English. Do not output any French words except in quoted code or file paths."
77
+ : "\n\n---\n\nINSTRUCTION FINALE (prime sur tout le reste): La reponse entiere, titres de sections inclus, DOIT etre en francais. Ne produis aucun mot anglais sauf dans du code ou des chemins de fichiers cites.";
78
+ return `${hint}\n\n${normalizeLabels(prompt, lang)}${final}`;
79
+ }
80
+
81
+ async function buildPrompt(name, args) {
82
+ const lang = (args.lang || "fr").toLowerCase();
83
+ const raw = getProjectPath(args.projectPath);
84
+ const projectPath = await resolveProjectPath(raw);
85
+ const focus = (args.focus || args.query || "").trim();
86
+ const style = (args.style || "punchy").toLowerCase();
87
+ const filePath = (args.filePath || "").trim();
88
+ const projectName = await getProjectName(projectPath);
89
+
90
+ let prompt = "";
91
+
92
+ switch (name) {
93
+ case "codebase_intelligence": {
94
+ const { context } = await collectIntelligenceContext(projectPath, focus, lang);
95
+ prompt = buildIntelligencePrompt(context, projectName, focus, style, lang);
96
+ break;
97
+ }
98
+ case "codebase_report": {
99
+ const { context } = await collectAssessmentContext(projectPath, focus, lang);
100
+ prompt = buildReportPrompt(context, projectName, focus, style, lang);
101
+ break;
102
+ }
103
+ case "codebase_audit": {
104
+ const { context } = await collectNonConformities(projectPath, focus, lang);
105
+ prompt = buildAuditPrompt(context, projectName, focus, lang);
106
+ break;
107
+ }
108
+ case "codebase_ceo": {
109
+ const { context } = await collectCeoContext(projectPath, focus || "one page executive brief", lang);
110
+ prompt = buildCeoPrompt(context, projectName, focus, lang);
111
+ break;
112
+ }
113
+ case "codebase_tasks": {
114
+ const { context } = await collectTasksContext(projectPath, focus || "plan d'action", lang);
115
+ prompt = buildTasksPrompt(context, projectName, focus, style, lang);
116
+ break;
117
+ }
118
+ case "codebase_player": {
119
+ const { context } = await collectCodebaseContext(projectPath, { focus: focus || "user journey and playthrough", lang });
120
+ prompt = buildPlayerPrompt(context, projectName, focus, lang);
121
+ break;
122
+ }
123
+ case "codebase_search": {
124
+ const { context } = await collectCodebaseContext(projectPath, { searchQuery: focus, lang });
125
+ prompt = buildSearchPrompt(focus, context, projectName, false, "", lang);
126
+ break;
127
+ }
128
+ case "codebase_explain": {
129
+ const target = filePath || focus;
130
+ const { context } = await collectCodebaseContext(projectPath, { focus: target, lang });
131
+ prompt = buildExplainPrompt(target, context, projectName, false, "", lang);
132
+ break;
133
+ }
134
+ case "codebase_refactor": {
135
+ const target = filePath || focus;
136
+ const { context } = await collectCodebaseContext(projectPath, { focus: target, filePath, lang });
137
+ prompt = buildRefactorPrompt(filePath, focus, context, projectName, false, "", lang);
138
+ break;
139
+ }
140
+ case "codebase_chat": {
141
+ const { context } = await collectCodebaseContext(projectPath, { focus, lang });
142
+ prompt = buildChatPrompt(focus, context, projectName, false, "", lang);
143
+ break;
144
+ }
145
+ case "codebase_crea": {
146
+ const { context } = await collectCodebaseContext(projectPath, { focus, lang });
147
+ prompt = buildCreaPrompt(focus, context, projectName, lang);
148
+ break;
149
+ }
150
+ default:
151
+ throw new Error(`Unknown tool: ${name}`);
152
+ }
153
+
154
+ return { prompt: wrapPrompt(prompt, lang), projectName };
155
+ }
156
+
157
+ const server = new Server(
158
+ { name: "dsh-codebase-chat-mcp", version: VERSION },
159
+ { capabilities: { tools: {} } }
160
+ );
161
+
162
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
163
+ return {
164
+ tools: [
165
+ { name: "codebase_intelligence", description: "Pro technical audit: architecture, tech debt, opportunities and creative ideas.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, style: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
166
+ { name: "codebase_report", description: "Strategic board report with SWOT, scorecards and 90-day roadmap.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, style: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
167
+ { name: "codebase_audit", description: "Non-conformities and technical-debt audit with concrete fixes.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
168
+ { name: "codebase_tasks", description: "Generate a prioritized TASKS.md with sprints and Before/After.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, style: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
169
+ { name: "codebase_ceo", description: "One-page executive brief with metrics, risks and killer moves.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
170
+ { name: "codebase_player", description: "User journey and playthrough UX analysis.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
171
+ { name: "codebase_search", description: "Search files and symbols by term or pattern.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, query: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } }, required: ["query"] } },
172
+ { name: "codebase_explain", description: "Explain how a file or symbol works.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, filePath: { type: "string" }, query: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
173
+ { name: "codebase_refactor", description: "Propose a refactor for a file or function.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, filePath: { type: "string" }, query: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
174
+ { name: "codebase_chat", description: "Open Q/A on the codebase.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, query: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } }, required: ["query"] } },
175
+ { name: "codebase_crea", description: "Generate creative ideas, slogans or marketing concepts from the code.", inputSchema: { type: "object", properties: { projectPath: { type: "string" }, focus: { type: "string" }, lang: { type: "string", enum: ["fr", "en"] } } } },
176
+ ],
177
+ };
178
+ });
179
+
180
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
181
+ const { name, arguments: args } = request.params;
182
+ try {
183
+ const { prompt } = await buildPrompt(name, args);
184
+ const content = await callLlm(prompt, args.lang);
185
+ return { content: [{ type: "text", text: content }] };
186
+ } catch (err) {
187
+ const msg = err instanceof Error ? err.message : String(err);
188
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
189
+ }
190
+ });
191
+
192
+ const transport = new StdioServerTransport();
193
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "dsh-codebase-chat-mcp",
3
+ "version": "0.2.1",
4
+ "description": "Standalone MCP server for dsh-codebase-chat. Works without DeepSeek Harness.",
5
+ "author": "shinzarou-eng",
6
+ "type": "module",
7
+ "bin": {
8
+ "dsh-codebase-chat-mcp": "./index.mjs"
9
+ },
10
+ "main": "./index.mjs",
11
+ "scripts": {
12
+ "start": "node index.mjs",
13
+ "test": "node test.mjs"
14
+ },
15
+ "files": [
16
+ "index.mjs",
17
+ "README.md",
18
+ "package.json"
19
+ ],
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.30.0",
22
+ "dsh-codebase-chat": "^0.16.0"
23
+ },
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "license": "MIT"
28
+ }