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.
- package/README.md +86 -0
- package/index.mjs +193 -0
- 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
|
+
}
|