phpstan-mcp-mx 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phpstan.js","sourceRoot":"","sources":["../src/phpstan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { Root } from './roots.js';
2
+ /** Een project dat door de whitelist is gekomen, met wat erin gevonden is. */
3
+ export interface ResolvedProject {
4
+ /** Geresolvede absolute projectroot. */
5
+ readonly path: string;
6
+ /** Absoluut pad naar vendor/bin/phpstan, of null als het niet geïnstalleerd is. */
7
+ readonly binary: string | null;
8
+ /** Bestandsnaam van de gevonden config, of null als het project er geen heeft. */
9
+ readonly config: string | null;
10
+ }
11
+ /** Een project zoals `list_projects` het rapporteert. */
12
+ export interface ProjectSummary {
13
+ readonly path: string;
14
+ /** De geconfigureerde root waaronder dit project gevonden is. */
15
+ readonly root: string;
16
+ readonly hasPhpstan: boolean;
17
+ readonly config: string | null;
18
+ }
19
+ /** Fout die als bruikbare melding naar het model teruggaat, niet als crash. */
20
+ export declare class ProjectError extends Error {
21
+ }
22
+ /**
23
+ * Zet een `project`-argument om in een projectroot die binnen de whitelist valt.
24
+ * Gooit een ProjectError met een melding die zegt wat er wél mag.
25
+ */
26
+ export declare function resolveProject(argument: string, roots: Root[]): ResolvedProject;
27
+ /**
28
+ * Zoekt binnen één projectmap het PHPStan-binary en de configuratie op. Doet geen
29
+ * whitelist-check; die hoort in resolveProject thuis.
30
+ */
31
+ export declare function describeProject(path: string): ResolvedProject;
32
+ /**
33
+ * Zet een pad-argument om in een absoluut pad binnen het project. Accepteert zowel
34
+ * een pad relatief aan de projectroot als een absoluut pad, want een model dat net
35
+ * `list_projects` heeft gezien geeft het ene door en een model dat een foutmelding
36
+ * heeft gelezen het andere.
37
+ */
38
+ export declare function resolveWithin(project: ResolvedProject, argument: string): string;
39
+ /**
40
+ * Loopt de roots langs en levert de PHP-projecten die erin liggen.
41
+ *
42
+ * Wel recursief, maar streng begrensd: er wordt niet verder gezocht binnen een map
43
+ * die zélf een project is (anders vind je elk pakket in vendor/ opnieuw), niet in
44
+ * vendor/, node_modules/ en verborgen mappen, en niet dieper dan MAX_DEPTH.
45
+ */
46
+ export declare function discoverProjects(roots: Root[]): ProjectSummary[];
@@ -0,0 +1,156 @@
1
+ import { readdirSync, realpathSync, statSync } from 'node:fs';
2
+ import { isAbsolute, join, relative } from 'node:path';
3
+ import { isUsable, ROOTS_ENV_VAR } from './roots.js';
4
+ /** Volgorde waarin PHPStan zelf zijn configuratie zoekt. */
5
+ const CONFIG_NAMES = ['phpstan.neon', 'phpstan.neon.dist'];
6
+ /** Pad naar het PHPStan-binary binnen een project, relatief aan de projectroot. */
7
+ const BINARY_PATH = join('vendor', 'bin', 'phpstan');
8
+ /** Mappen die nooit een PHP-project zijn en die we bij het zoeken overslaan. */
9
+ const SKIP_DIRS = new Set(['vendor', 'node_modules', 'storage', 'public']);
10
+ /** Fout die als bruikbare melding naar het model teruggaat, niet als crash. */
11
+ export class ProjectError extends Error {
12
+ }
13
+ /**
14
+ * Zet een `project`-argument om in een projectroot die binnen de whitelist valt.
15
+ * Gooit een ProjectError met een melding die zegt wat er wél mag.
16
+ */
17
+ export function resolveProject(argument, roots) {
18
+ const usable = roots.filter(isUsable);
19
+ if (usable.length === 0) {
20
+ throw new ProjectError(`Deze server heeft geen bruikbare projectmappen. Zet ${ROOTS_ENV_VAR} in de ` +
21
+ 'configuratie van de MCP-server op de absolute paden die geanalyseerd mogen worden.');
22
+ }
23
+ if (!isAbsolute(argument)) {
24
+ throw new ProjectError(`"${argument}" is geen absoluut pad. Geef het volledige pad naar de projectmap.`);
25
+ }
26
+ let path;
27
+ try {
28
+ path = realpathSync.native(argument);
29
+ if (!statSync(path).isDirectory()) {
30
+ throw new ProjectError(`"${argument}" is geen map.`);
31
+ }
32
+ }
33
+ catch (error) {
34
+ if (error instanceof ProjectError)
35
+ throw error;
36
+ throw new ProjectError(`"${argument}" bestaat niet.`);
37
+ }
38
+ // De containment-check draait op het geresolvede pad, ná realpath: anders komt
39
+ // een ".."-pad of een symlink naar buiten de whitelist er alsnog doorheen.
40
+ if (!usable.some((root) => contains(root.path, path))) {
41
+ const allowed = usable.map((root) => ` - ${root.path}`).join('\n');
42
+ throw new ProjectError(`"${path}" valt buiten de toegestane projectmappen. Toegestaan is alles onder:\n${allowed}`);
43
+ }
44
+ return describeProject(path);
45
+ }
46
+ /**
47
+ * Zoekt binnen één projectmap het PHPStan-binary en de configuratie op. Doet geen
48
+ * whitelist-check; die hoort in resolveProject thuis.
49
+ */
50
+ export function describeProject(path) {
51
+ const binary = join(path, BINARY_PATH);
52
+ return {
53
+ path,
54
+ binary: isFile(binary) ? binary : null,
55
+ config: CONFIG_NAMES.find((name) => isFile(join(path, name))) ?? null,
56
+ };
57
+ }
58
+ /**
59
+ * Zet een pad-argument om in een absoluut pad binnen het project. Accepteert zowel
60
+ * een pad relatief aan de projectroot als een absoluut pad, want een model dat net
61
+ * `list_projects` heeft gezien geeft het ene door en een model dat een foutmelding
62
+ * heeft gelezen het andere.
63
+ */
64
+ export function resolveWithin(project, argument) {
65
+ if (argument.trim() === '') {
66
+ throw new ProjectError('Geef een pad binnen het project op.');
67
+ }
68
+ const candidate = isAbsolute(argument) ? argument : join(project.path, argument);
69
+ let path;
70
+ try {
71
+ path = realpathSync.native(candidate);
72
+ }
73
+ catch {
74
+ throw new ProjectError(`"${argument}" bestaat niet in ${project.path}.`);
75
+ }
76
+ // Dezelfde volgorde als in resolveProject: pas ná realpath vergelijken, anders
77
+ // glipt een ".."-pad of een symlink er alsnog doorheen.
78
+ if (!contains(project.path, path)) {
79
+ throw new ProjectError(`"${argument}" ligt buiten het project ${project.path}.`);
80
+ }
81
+ return path;
82
+ }
83
+ /**
84
+ * Hoe diep er onder een root gezocht wordt. Twee is niet genoeg: een indeling als
85
+ * ~/projecten/<groep>/<project> zit al op niveau twee, en dan is er geen ruimte
86
+ * meer voor een tussenmap. Vier geeft die ruimte zonder dat het zoeken ontspoort.
87
+ */
88
+ const MAX_DEPTH = 4;
89
+ /**
90
+ * Loopt de roots langs en levert de PHP-projecten die erin liggen.
91
+ *
92
+ * Wel recursief, maar streng begrensd: er wordt niet verder gezocht binnen een map
93
+ * die zélf een project is (anders vind je elk pakket in vendor/ opnieuw), niet in
94
+ * vendor/, node_modules/ en verborgen mappen, en niet dieper dan MAX_DEPTH.
95
+ */
96
+ export function discoverProjects(roots) {
97
+ const projects = [];
98
+ for (const root of roots) {
99
+ if (root.path === null)
100
+ continue;
101
+ collectProjects(root.path, root.path, 0, projects);
102
+ }
103
+ return projects;
104
+ }
105
+ function collectProjects(path, root, depth, found) {
106
+ if (isProjectDir(path)) {
107
+ found.push(summarise(path, root));
108
+ return;
109
+ }
110
+ if (depth >= MAX_DEPTH)
111
+ return;
112
+ for (const child of childDirectories(path)) {
113
+ collectProjects(child, root, depth + 1, found);
114
+ }
115
+ }
116
+ function summarise(path, root) {
117
+ const { binary, config } = describeProject(path);
118
+ return { path, root, hasPhpstan: binary !== null, config };
119
+ }
120
+ /** Een map telt als PHP-project zodra er een composer.json in staat. */
121
+ function isProjectDir(path) {
122
+ return isFile(join(path, 'composer.json'));
123
+ }
124
+ function childDirectories(path) {
125
+ try {
126
+ // isDirectory() is false voor een symlink, dus symlinks worden niet gevolgd.
127
+ // Bewust: een root uitlezen mag nooit buiten die root uitkomen. Een project
128
+ // dat via een symlink in een root hangt is nog steeds analyseerbaar — het pad
129
+ // moet dan alleen expliciet aan resolveProject meegegeven worden.
130
+ return readdirSync(path, { withFileTypes: true })
131
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.') && !SKIP_DIRS.has(entry.name))
132
+ .map((entry) => join(path, entry.name));
133
+ }
134
+ catch {
135
+ return [];
136
+ }
137
+ }
138
+ function isFile(path) {
139
+ try {
140
+ return statSync(path).isFile();
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ /**
147
+ * Of `child` binnen `parent` valt. Via path.relative en niet via startsWith,
148
+ * want "/a/foobar" begint óók met "/a/foo".
149
+ */
150
+ function contains(parent, child) {
151
+ if (parent === child)
152
+ return true;
153
+ const rel = relative(parent, child);
154
+ return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
155
+ }
156
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import type { AnalysisResult, Finding } from './phpstan.js';
2
+ /** Standaard aantal bevindingen per tool-aanroep. */
3
+ export declare const DEFAULT_LIMIT = 50;
4
+ /** Bovengrens, zodat één aanroep de context niet alsnog kan vullen. */
5
+ export declare const MAX_LIMIT = 500;
6
+ /** Een bevinding zoals ze naar buiten gaat: pad relatief waar dat kan. */
7
+ export interface DisplayFinding extends Omit<Finding, 'file'> {
8
+ readonly file: string;
9
+ }
10
+ /** Welk deel van de bevindingen dit antwoord bevat. */
11
+ export interface Page {
12
+ readonly offset: number;
13
+ readonly limit: number;
14
+ readonly returned: number;
15
+ readonly total: number;
16
+ readonly hasMore: boolean;
17
+ }
18
+ /** Tellingen over álle bevindingen, ook die buiten deze pagina vallen. */
19
+ export interface Summary {
20
+ readonly filesWithFindings: number;
21
+ readonly byFile: readonly {
22
+ file: string;
23
+ count: number;
24
+ }[];
25
+ readonly byIdentifier: readonly {
26
+ identifier: string;
27
+ count: number;
28
+ }[];
29
+ }
30
+ /** Het volledige antwoord van een analyse-tool. */
31
+ export interface Report {
32
+ readonly project: string;
33
+ readonly totals: AnalysisResult['totals'];
34
+ readonly generalErrors: readonly string[];
35
+ readonly summary: Summary;
36
+ readonly findings: readonly DisplayFinding[];
37
+ readonly page: Page;
38
+ }
39
+ /** Waar in de bevindingenlijst dit antwoord begint en hoe lang het is. */
40
+ export interface Window {
41
+ readonly offset?: number | undefined;
42
+ readonly limit?: number | undefined;
43
+ }
44
+ /**
45
+ * Zet een analyse om in het antwoord dat de tool teruggeeft: tellingen over alles,
46
+ * en maar één pagina bevindingen. De samenvatting rekent bewust over de volledige
47
+ * lijst — anders zou de tweede pagina andere totalen melden dan de eerste.
48
+ */
49
+ export declare function buildReport(result: AnalysisResult, window?: Window): Report;
50
+ /** Schrijft een rapport uit als tekst voor het model. */
51
+ export declare function renderReport(report: Report): string;
52
+ /**
53
+ * Pad zoals het aan het model getoond wordt: relatief aan de projectroot, tenzij
54
+ * het bestand daarbuiten ligt. PHPStan rapporteert ook fouten in vendor/ of in
55
+ * paden die van buiten de root geïncludeerd zijn, en een reeks ../../.. is daar
56
+ * onleesbaarder dan gewoon het absolute pad.
57
+ */
58
+ export declare function displayPath(project: string, file: string): string;
package/dist/report.js ADDED
@@ -0,0 +1,111 @@
1
+ import { isAbsolute, relative } from 'node:path';
2
+ /** Standaard aantal bevindingen per tool-aanroep. */
3
+ export const DEFAULT_LIMIT = 50;
4
+ /** Bovengrens, zodat één aanroep de context niet alsnog kan vullen. */
5
+ export const MAX_LIMIT = 500;
6
+ /** Hoeveel regels de samenvattingslijsten hoogstens tonen. */
7
+ const SUMMARY_ROWS = 15;
8
+ /**
9
+ * Zet een analyse om in het antwoord dat de tool teruggeeft: tellingen over alles,
10
+ * en maar één pagina bevindingen. De samenvatting rekent bewust over de volledige
11
+ * lijst — anders zou de tweede pagina andere totalen melden dan de eerste.
12
+ */
13
+ export function buildReport(result, window = {}) {
14
+ const all = result.findings.map((finding) => ({
15
+ ...finding,
16
+ file: displayPath(result.project, finding.file),
17
+ }));
18
+ const limit = clamp(window.limit ?? DEFAULT_LIMIT, 1, MAX_LIMIT);
19
+ const offset = clamp(window.offset ?? 0, 0, Math.max(all.length, 0));
20
+ const findings = all.slice(offset, offset + limit);
21
+ return {
22
+ project: result.project,
23
+ totals: result.totals,
24
+ generalErrors: result.generalErrors,
25
+ summary: summarise(all),
26
+ findings,
27
+ page: {
28
+ offset,
29
+ limit,
30
+ returned: findings.length,
31
+ total: all.length,
32
+ hasMore: offset + findings.length < all.length,
33
+ },
34
+ };
35
+ }
36
+ /** Schrijft een rapport uit als tekst voor het model. */
37
+ export function renderReport(report) {
38
+ const lines = [];
39
+ if (report.generalErrors.length > 0) {
40
+ lines.push('Fouten buiten de bestanden om:');
41
+ for (const error of report.generalErrors)
42
+ lines.push(`- ${error}`);
43
+ lines.push('');
44
+ }
45
+ if (report.page.total === 0) {
46
+ lines.push(report.generalErrors.length === 0 ? 'Geen bevindingen.' : 'Verder geen bevindingen.');
47
+ return lines.join('\n');
48
+ }
49
+ lines.push(`${report.page.total} bevinding(en) in ${report.summary.filesWithFindings} bestand(en).`, '', 'Meest voorkomend:', ...report.summary.byIdentifier.map((row) => ` ${pad(row.count)} ${row.identifier}`));
50
+ if (report.summary.byFile.length > 1) {
51
+ lines.push('', 'Zwaarst getroffen bestanden:', ...report.summary.byFile.map((row) => ` ${pad(row.count)} ${row.file}`));
52
+ }
53
+ const from = report.page.offset + 1;
54
+ const to = report.page.offset + report.page.returned;
55
+ lines.push('', `Bevindingen ${from}-${to} van ${report.page.total}:`);
56
+ let current = null;
57
+ for (const finding of report.findings) {
58
+ if (finding.file !== current) {
59
+ current = finding.file;
60
+ lines.push('', current);
61
+ }
62
+ const where = finding.line === null ? ' ?' : String(finding.line).padStart(4);
63
+ lines.push(`${where} ${finding.identifier ?? '-'} ${finding.message}`);
64
+ if (finding.tip !== null)
65
+ lines.push(` tip: ${finding.tip}`);
66
+ }
67
+ if (report.page.hasMore) {
68
+ lines.push('', `Nog ${report.page.total - to} bevinding(en). Roep dezelfde tool opnieuw aan met ` +
69
+ `offset=${to} voor de rest.`);
70
+ }
71
+ return lines.join('\n');
72
+ }
73
+ function summarise(findings) {
74
+ const perFile = new Map();
75
+ const perIdentifier = new Map();
76
+ for (const finding of findings) {
77
+ perFile.set(finding.file, (perFile.get(finding.file) ?? 0) + 1);
78
+ const identifier = finding.identifier ?? '(zonder identifier)';
79
+ perIdentifier.set(identifier, (perIdentifier.get(identifier) ?? 0) + 1);
80
+ }
81
+ return {
82
+ filesWithFindings: perFile.size,
83
+ byFile: topRows(perFile).map(([file, count]) => ({ file, count })),
84
+ byIdentifier: topRows(perIdentifier).map(([identifier, count]) => ({ identifier, count })),
85
+ };
86
+ }
87
+ /** De drukste regels eerst, bij gelijk aantal alfabetisch zodat de volgorde vastligt. */
88
+ function topRows(counts) {
89
+ return [...counts.entries()]
90
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
91
+ .slice(0, SUMMARY_ROWS);
92
+ }
93
+ /**
94
+ * Pad zoals het aan het model getoond wordt: relatief aan de projectroot, tenzij
95
+ * het bestand daarbuiten ligt. PHPStan rapporteert ook fouten in vendor/ of in
96
+ * paden die van buiten de root geïncludeerd zijn, en een reeks ../../.. is daar
97
+ * onleesbaarder dan gewoon het absolute pad.
98
+ */
99
+ export function displayPath(project, file) {
100
+ const rel = relative(project, file);
101
+ if (rel === '' || rel.startsWith('..') || isAbsolute(rel))
102
+ return file;
103
+ return rel;
104
+ }
105
+ function clamp(value, low, high) {
106
+ return Math.min(Math.max(Math.trunc(value), low), high);
107
+ }
108
+ function pad(count) {
109
+ return String(count).padStart(4);
110
+ }
111
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** Env-variabele met de projectmappen die deze server mag analyseren. */
2
+ export declare const ROOTS_ENV_VAR = "PHPSTAN_MCP_ROOTS";
3
+ /** Eén geconfigureerde root, na normalisatie. */
4
+ export interface Root {
5
+ /** Zoals opgegeven in de env-variabele, ongewijzigd — voor foutmeldingen. */
6
+ readonly raw: string;
7
+ /** Het volledig geresolvede pad, of null als de root onbruikbaar is. */
8
+ readonly path: string | null;
9
+ /** Waarom de root onbruikbaar is, of null als er niets mis is. */
10
+ readonly problem: string | null;
11
+ }
12
+ /**
13
+ * Leest de toegestane roots uit de omgeving. Onbruikbare regels worden niet
14
+ * stilzwijgend genegeerd maar meegegeven met een reden, zodat `list_projects`
15
+ * kan uitleggen waarom een verwacht project ontbreekt.
16
+ */
17
+ export declare function loadRoots(env?: NodeJS.ProcessEnv): Root[];
18
+ /** Een root waarvan het pad geresolvet is en die dus daadwerkelijk bruikbaar is. */
19
+ export type UsableRoot = Root & {
20
+ path: string;
21
+ };
22
+ /** Type-guard zodat `roots.filter(isUsable)` het null-pad wegneemt. */
23
+ export declare function isUsable(root: Root): root is UsableRoot;
package/dist/roots.js ADDED
@@ -0,0 +1,55 @@
1
+ import { realpathSync, statSync } from 'node:fs';
2
+ import { delimiter, isAbsolute } from 'node:path';
3
+ /** Env-variabele met de projectmappen die deze server mag analyseren. */
4
+ export const ROOTS_ENV_VAR = 'PHPSTAN_MCP_ROOTS';
5
+ /**
6
+ * Leest de toegestane roots uit de omgeving. Onbruikbare regels worden niet
7
+ * stilzwijgend genegeerd maar meegegeven met een reden, zodat `list_projects`
8
+ * kan uitleggen waarom een verwacht project ontbreekt.
9
+ */
10
+ export function loadRoots(env = process.env) {
11
+ const configured = env[ROOTS_ENV_VAR];
12
+ if (configured === undefined || configured.trim() === '')
13
+ return [];
14
+ const roots = [];
15
+ const seen = new Set();
16
+ // path.delimiter in plaats van een harde ':' — op Windows is dat ';', en een
17
+ // Windows-pad bevat zelf een dubbele punt na de driveletter.
18
+ for (const entry of configured.split(delimiter)) {
19
+ const raw = entry.trim();
20
+ if (raw === '')
21
+ continue;
22
+ const root = inspectRoot(raw);
23
+ // Dedupliceren op het geresolvede pad: twee verschillende regels kunnen via
24
+ // een symlink dezelfde map zijn.
25
+ const key = root.path ?? `raw:${raw}`;
26
+ if (seen.has(key))
27
+ continue;
28
+ seen.add(key);
29
+ roots.push(root);
30
+ }
31
+ return roots;
32
+ }
33
+ function inspectRoot(raw) {
34
+ if (!isAbsolute(raw)) {
35
+ return { raw, path: null, problem: 'geen absoluut pad' };
36
+ }
37
+ try {
38
+ // realpathSync.native en niet de JS-variant: alleen de native versie
39
+ // corrigeert ook de hoofdlettervorm op een case-insensitive volume (macOS),
40
+ // en die vorm moet kloppen omdat de containment-check erop vergelijkt.
41
+ const path = realpathSync.native(raw);
42
+ if (!statSync(path).isDirectory()) {
43
+ return { raw, path: null, problem: 'is geen map' };
44
+ }
45
+ return { raw, path, problem: null };
46
+ }
47
+ catch {
48
+ return { raw, path: null, problem: 'bestaat niet' };
49
+ }
50
+ }
51
+ /** Type-guard zodat `roots.filter(isUsable)` het null-pad wegneemt. */
52
+ export function isUsable(root) {
53
+ return root.path !== null;
54
+ }
55
+ //# sourceMappingURL=roots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roots.js","sourceRoot":"","sources":["../src/roots.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAElD,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAYjD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC5D,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC;IACtC,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAEpE,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,6EAA6E;IAC7E,6DAA6D;IAC7D,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QACzB,IAAI,GAAG,KAAK,EAAE;YAAE,SAAS;QAEzB,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC9B,4EAA4E;QAC5E,iCAAiC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,GAAG,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEd,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,CAAC;QACH,qEAAqE;QACrE,4EAA4E;QAC5E,uEAAuE;QACvE,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAClC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;QACrD,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IACtD,CAAC;AACH,CAAC;AAKD,uEAAuE;AACvE,MAAM,UAAU,QAAQ,CAAC,IAAU;IACjC,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Root } from './roots.js';
3
+ /**
4
+ * Versie van de server zoals gemeld in de MCP-handshake. Spiegelt `version` in
5
+ * package.json; die kan hier niet zomaar geïmporteerd worden, want een JSON-import
6
+ * verhuist bij het bouwen mee naar dist/ en wijst dan naar het verkeerde bestand.
7
+ */
8
+ export declare const VERSION = "0.1.0";
9
+ /**
10
+ * Bouwt de MCP-server met de tools die op deze set roots van toepassing zijn.
11
+ * De roots worden één keer bij het opstarten ingelezen: ze komen uit de
12
+ * serverconfiguratie, niet uit een tool-argument, en horen dus niet per aanroep
13
+ * te kunnen wijzigen.
14
+ *
15
+ * Staat bewust los van index.ts: dat bestand start bij import meteen de server,
16
+ * en een test die alleen de tools wil aanspreken moet dat niet in gang zetten.
17
+ */
18
+ export declare function createServer(roots: Root[]): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,27 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { registerTools } from './tools.js';
3
+ /**
4
+ * Versie van de server zoals gemeld in de MCP-handshake. Spiegelt `version` in
5
+ * package.json; die kan hier niet zomaar geïmporteerd worden, want een JSON-import
6
+ * verhuist bij het bouwen mee naar dist/ en wijst dan naar het verkeerde bestand.
7
+ */
8
+ export const VERSION = '0.1.0';
9
+ /**
10
+ * Bouwt de MCP-server met de tools die op deze set roots van toepassing zijn.
11
+ * De roots worden één keer bij het opstarten ingelezen: ze komen uit de
12
+ * serverconfiguratie, niet uit een tool-argument, en horen dus niet per aanroep
13
+ * te kunnen wijzigen.
14
+ *
15
+ * Staat bewust los van index.ts: dat bestand start bij import meteen de server,
16
+ * en een test die alleen de tools wil aanspreken moet dat niet in gang zetten.
17
+ */
18
+ export function createServer(roots) {
19
+ const server = new McpServer({ name: 'phpstan-mcp', version: VERSION }, {
20
+ instructions: 'Statische analyse van PHP-projecten met PHPStan. Elke tool verwacht een ' +
21
+ 'expliciet `project`-pad; de server heeft geen "huidig project". Roep ' +
22
+ 'list_projects aan om te zien welke paden geldig zijn.',
23
+ });
24
+ registerTools(server, roots);
25
+ return server;
26
+ }
27
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,EACzC;QACE,YAAY,EACV,0EAA0E;YAC1E,uEAAuE;YACvE,uDAAuD;KAC1D,CACF,CAAC;IAEF,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Root } from './roots.js';
3
+ /** Hangt alle tools aan de server. */
4
+ export declare function registerTools(server: McpServer, roots: Root[]): void;