@tsrx/mcp 0.0.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.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/package.json +51 -0
- package/src/analyze.js +195 -0
- package/src/compile.js +235 -0
- package/src/docs.js +50 -0
- package/src/format.js +138 -0
- package/src/generated/docs.js +79 -0
- package/src/http.js +99 -0
- package/src/index.js +25 -0
- package/src/inspect.js +235 -0
- package/src/schemas.js +141 -0
- package/src/server.js +629 -0
- package/src/stdio.js +8 -0
- package/src/target.js +216 -0
- package/src/validate.js +112 -0
package/src/server.js
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import {
|
|
6
|
+
find_documentation_section,
|
|
7
|
+
find_similar_documentation_sections,
|
|
8
|
+
list_documentation_sections,
|
|
9
|
+
} from './docs.js';
|
|
10
|
+
import { analyze_tsrx } from './analyze.js';
|
|
11
|
+
import { compile_tsrx } from './compile.js';
|
|
12
|
+
import { format_tsrx } from './format.js';
|
|
13
|
+
import { inspect_project } from './inspect.js';
|
|
14
|
+
import { validate_tsrx_file } from './validate.js';
|
|
15
|
+
import { detect_target } from './target.js';
|
|
16
|
+
import {
|
|
17
|
+
TARGET_SCHEMA,
|
|
18
|
+
analysis_result_schema,
|
|
19
|
+
compile_result_schema,
|
|
20
|
+
format_result_schema,
|
|
21
|
+
inspect_project_result_schema,
|
|
22
|
+
target_detection_schema,
|
|
23
|
+
validate_file_result_schema,
|
|
24
|
+
} from './schemas.js';
|
|
25
|
+
|
|
26
|
+
const package_json_path = fileURLToPath(new URL('../package.json', import.meta.url));
|
|
27
|
+
const package_json = JSON.parse(readFileSync(package_json_path, 'utf8'));
|
|
28
|
+
|
|
29
|
+
const SERVER_INFO = {
|
|
30
|
+
name: 'TSRX MCP Server',
|
|
31
|
+
version: typeof package_json.version === 'string' ? package_json.version : '0.0.0',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const TARGET_RESOURCE_CONTENT = {
|
|
35
|
+
react: `# TSRX React Target
|
|
36
|
+
|
|
37
|
+
The core TSRX MCP server owns target-neutral language syntax and compiler validation. React-specific guidance should live in a React target layer.
|
|
38
|
+
|
|
39
|
+
The React target layer should own:
|
|
40
|
+
- React package and bundler setup
|
|
41
|
+
- React runtime imports and helper APIs
|
|
42
|
+
- React JSX compatibility expectations
|
|
43
|
+
- React-specific compiler diagnostics and examples
|
|
44
|
+
- interop with React libraries and hooks
|
|
45
|
+
|
|
46
|
+
Before giving React-specific advice, use \`detect-target\` or an explicit target signal and validate generated .tsrx code with \`compile-tsrx\`.`,
|
|
47
|
+
preact: `# TSRX Preact Target
|
|
48
|
+
|
|
49
|
+
The core TSRX MCP server owns target-neutral language syntax and compiler validation. Preact-specific guidance should live in a Preact target layer.
|
|
50
|
+
|
|
51
|
+
The Preact target layer should own:
|
|
52
|
+
- Preact package and bundler setup
|
|
53
|
+
- Preact runtime imports and helper APIs
|
|
54
|
+
- Preact JSX compatibility expectations
|
|
55
|
+
- Preact-specific compiler diagnostics and examples
|
|
56
|
+
- interop with Preact libraries and hooks
|
|
57
|
+
|
|
58
|
+
Before giving Preact-specific advice, use \`detect-target\` or an explicit target signal and validate generated .tsrx code with \`compile-tsrx\`.`,
|
|
59
|
+
solid: `# TSRX Solid Target
|
|
60
|
+
|
|
61
|
+
The core TSRX MCP server owns target-neutral language syntax and compiler validation. Solid-specific guidance should live in a Solid target layer.
|
|
62
|
+
|
|
63
|
+
The Solid target layer should own:
|
|
64
|
+
- Solid package and bundler setup
|
|
65
|
+
- Solid runtime imports and helper APIs
|
|
66
|
+
- Solid JSX compatibility expectations
|
|
67
|
+
- Solid-specific compiler diagnostics and examples
|
|
68
|
+
- interop with Solid primitives and libraries
|
|
69
|
+
|
|
70
|
+
Before giving Solid-specific advice, use \`detect-target\` or an explicit target signal and validate generated .tsrx code with \`compile-tsrx\`.`,
|
|
71
|
+
vue: `# TSRX Vue Target
|
|
72
|
+
|
|
73
|
+
The core TSRX MCP server owns target-neutral language syntax and compiler validation. Vue-specific guidance should live in a Vue target layer.
|
|
74
|
+
|
|
75
|
+
The Vue target layer should own:
|
|
76
|
+
- Vue package and bundler setup
|
|
77
|
+
- Vue runtime imports and helper APIs
|
|
78
|
+
- Vue JSX/Vapor compatibility expectations
|
|
79
|
+
- Vue-specific compiler diagnostics and examples
|
|
80
|
+
- interop with Vue libraries and composition APIs
|
|
81
|
+
|
|
82
|
+
Before giving Vue-specific advice, use \`detect-target\` or an explicit target signal and validate generated .tsrx code with \`compile-tsrx\`.`,
|
|
83
|
+
ripple: `# TSRX Ripple Target
|
|
84
|
+
|
|
85
|
+
The core TSRX MCP server owns target-neutral language syntax and compiler validation. Ripple-specific guidance should live in a Ripple target layer.
|
|
86
|
+
|
|
87
|
+
The Ripple target layer should own:
|
|
88
|
+
- Ripple package and bundler setup
|
|
89
|
+
- Ripple runtime imports and helper APIs
|
|
90
|
+
- Ripple reactivity, server, and hydration semantics
|
|
91
|
+
- Ripple-specific compiler diagnostics and examples
|
|
92
|
+
- interop with Ripple runtime components and helpers
|
|
93
|
+
|
|
94
|
+
Before giving Ripple-specific advice, use \`detect-target\` or an explicit target signal and validate generated .tsrx code with \`compile-tsrx\`.`,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {unknown} value
|
|
99
|
+
* @returns {string[]}
|
|
100
|
+
*/
|
|
101
|
+
function normalize_section_input(value) {
|
|
102
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === 'string');
|
|
103
|
+
if (typeof value !== 'string') return [];
|
|
104
|
+
const trimmed = value.trim();
|
|
105
|
+
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(trimmed);
|
|
108
|
+
if (Array.isArray(parsed)) return parsed.filter((item) => typeof item === 'string');
|
|
109
|
+
} catch {
|
|
110
|
+
// Fall back to comma-separated handling.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return trimmed
|
|
114
|
+
.split(',')
|
|
115
|
+
.map((section) => section.trim())
|
|
116
|
+
.filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function list_sections_handler() {
|
|
120
|
+
return {
|
|
121
|
+
sections: list_documentation_sections().map(({ title, slug, use_cases }) => ({
|
|
122
|
+
title,
|
|
123
|
+
slug,
|
|
124
|
+
use_cases,
|
|
125
|
+
})),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {{ section: string | string[] }} input
|
|
131
|
+
*/
|
|
132
|
+
export function get_documentation_handler(input) {
|
|
133
|
+
const requested_sections = normalize_section_input(input.section);
|
|
134
|
+
const found = [];
|
|
135
|
+
const missing = [];
|
|
136
|
+
|
|
137
|
+
for (const requested of requested_sections) {
|
|
138
|
+
const section = find_documentation_section(requested);
|
|
139
|
+
if (section) {
|
|
140
|
+
found.push(section);
|
|
141
|
+
} else {
|
|
142
|
+
missing.push({
|
|
143
|
+
section: requested,
|
|
144
|
+
similar: find_similar_documentation_sections(requested).map(({ title, slug }) => ({
|
|
145
|
+
title,
|
|
146
|
+
slug,
|
|
147
|
+
})),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
sections: found.map(({ title, slug, content }) => ({ title, slug, content })),
|
|
154
|
+
missing,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {{ cwd?: string }} input
|
|
160
|
+
*/
|
|
161
|
+
export function detect_target_handler(input = {}) {
|
|
162
|
+
return detect_target(input.cwd);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param {{
|
|
167
|
+
* code: string,
|
|
168
|
+
* filename?: string,
|
|
169
|
+
* target?: string,
|
|
170
|
+
* cwd?: string,
|
|
171
|
+
* collect?: boolean,
|
|
172
|
+
* loose?: boolean,
|
|
173
|
+
* includeCode?: boolean,
|
|
174
|
+
* mode?: 'client' | 'server'
|
|
175
|
+
* }} input
|
|
176
|
+
*/
|
|
177
|
+
export function compile_tsrx_handler(input) {
|
|
178
|
+
return compile_tsrx(input);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {{
|
|
183
|
+
* code: string,
|
|
184
|
+
* filename?: string,
|
|
185
|
+
* target?: string,
|
|
186
|
+
* cwd?: string,
|
|
187
|
+
* collect?: boolean,
|
|
188
|
+
* loose?: boolean,
|
|
189
|
+
* mode?: 'client' | 'server'
|
|
190
|
+
* }} input
|
|
191
|
+
*/
|
|
192
|
+
export function analyze_tsrx_handler(input) {
|
|
193
|
+
return analyze_tsrx(input);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @param {{
|
|
198
|
+
* code: string,
|
|
199
|
+
* filename?: string,
|
|
200
|
+
* cwd?: string,
|
|
201
|
+
* printWidth?: number,
|
|
202
|
+
* tabWidth?: number,
|
|
203
|
+
* useTabs?: boolean,
|
|
204
|
+
* singleQuote?: boolean,
|
|
205
|
+
* check?: boolean
|
|
206
|
+
* }} input
|
|
207
|
+
*/
|
|
208
|
+
export function format_tsrx_handler(input) {
|
|
209
|
+
return format_tsrx(input);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param {{ cwd?: string }} input
|
|
214
|
+
*/
|
|
215
|
+
export function inspect_project_handler(input = {}) {
|
|
216
|
+
return inspect_project(input);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {{
|
|
221
|
+
* filePath: string,
|
|
222
|
+
* cwd?: string,
|
|
223
|
+
* target?: string,
|
|
224
|
+
* collect?: boolean,
|
|
225
|
+
* loose?: boolean,
|
|
226
|
+
* mode?: 'client' | 'server',
|
|
227
|
+
* printWidth?: number,
|
|
228
|
+
* tabWidth?: number,
|
|
229
|
+
* useTabs?: boolean,
|
|
230
|
+
* singleQuote?: boolean
|
|
231
|
+
* }} input
|
|
232
|
+
*/
|
|
233
|
+
export function validate_tsrx_file_handler(input) {
|
|
234
|
+
return validate_tsrx_file(input);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {unknown} value
|
|
239
|
+
*/
|
|
240
|
+
function json_text(value) {
|
|
241
|
+
return JSON.stringify(value, null, 2);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @param {string} uri
|
|
246
|
+
* @param {string} text
|
|
247
|
+
*/
|
|
248
|
+
function text_resource(uri, text) {
|
|
249
|
+
return {
|
|
250
|
+
contents: [
|
|
251
|
+
{
|
|
252
|
+
uri,
|
|
253
|
+
mimeType: 'text/markdown',
|
|
254
|
+
text,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @param {{ remote: boolean }} options
|
|
262
|
+
*/
|
|
263
|
+
function create_tsrx_task_prompt(options) {
|
|
264
|
+
const project_context_step = options.remote
|
|
265
|
+
? '2. This hosted MCP endpoint cannot inspect a local project filesystem. Use an explicit `target` argument when compiling or analyzing code, or ask the user which target runtime they use.'
|
|
266
|
+
: '2. If project context exists, call `inspect-project` for package/tooling context or `detect-target` when only the runtime target is needed before assuming React, Preact, Solid, Vue, or Ripple semantics.';
|
|
267
|
+
const file_validation_step = options.remote
|
|
268
|
+
? '6. For existing files, ask the user to paste source or use a local stdio MCP client; hosted MCP cannot read local file paths.'
|
|
269
|
+
: '6. When working with an existing file, call `validate-tsrx-file` for one-shot format, compile, and advice feedback.';
|
|
270
|
+
const compile_step = options.remote
|
|
271
|
+
? '7. Before presenting generated .tsrx code as final, call `format-tsrx`, then `compile-tsrx` with an explicit target.'
|
|
272
|
+
: '7. Before presenting generated .tsrx code as final, call `format-tsrx`, then `compile-tsrx` with the inferred or explicit target.';
|
|
273
|
+
|
|
274
|
+
return `Use this workflow when helping with TSRX:
|
|
275
|
+
|
|
276
|
+
1. Identify whether the task is about target-neutral TSRX syntax, target runtime behavior, or both.
|
|
277
|
+
${project_context_step}
|
|
278
|
+
3. For syntax uncertainty, use \`list-sections\`, \`get-documentation\`, or read \`tsrx://docs/{slug}.md\`.
|
|
279
|
+
4. Keep core TSRX advice target-neutral: component declarations, statement templates, control flow, TSX expression values, lazy destructuring, style identifiers, and server blocks.
|
|
280
|
+
5. Use \`tsrx://targets/{target}.md\` as the handoff point for target-specific responsibilities.
|
|
281
|
+
${file_validation_step}
|
|
282
|
+
${compile_step}
|
|
283
|
+
8. If \`compile-tsrx\` returns diagnostics, call \`analyze-tsrx\` for targeted authoring advice, fix the code, format it, and compile again.
|
|
284
|
+
9. Do not invent runtime APIs, imports, or bundler configuration from target-neutral TSRX docs. Use target-specific docs, resources, or skills for those details.`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Create the shared TSRX MCP server. Transports are intentionally owned by wrapper
|
|
289
|
+
* packages so this package can be reused by stdio and hosted HTTP entry points.
|
|
290
|
+
*
|
|
291
|
+
* @param {{ remote?: boolean }} [options]
|
|
292
|
+
*/
|
|
293
|
+
export function createTSRXMcpServer(options = {}) {
|
|
294
|
+
const remote = options.remote === true;
|
|
295
|
+
const server = new McpServer(SERVER_INFO, {
|
|
296
|
+
instructions: remote
|
|
297
|
+
? 'Use this hosted server for target-neutral TSRX language guidance. It cannot inspect local project files, so pass an explicit runtime target before compiling .tsrx code. Fetch relevant docs sections when syntax details are uncertain, and use target-specific docs or resources for runtime APIs, imports, bundler setup, and framework semantics.'
|
|
298
|
+
: 'Use this server for target-neutral TSRX language guidance. Detect the runtime target before generating .tsrx code, fetch relevant docs sections when syntax details are uncertain, and use target-specific skills or resources for runtime APIs, imports, bundler setup, and framework semantics.',
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
server.registerTool(
|
|
302
|
+
'list-sections',
|
|
303
|
+
{
|
|
304
|
+
title: 'List TSRX Documentation Sections',
|
|
305
|
+
description:
|
|
306
|
+
'Lists available TSRX documentation sections with use_cases. Use this to discover relevant docs before answering TSRX syntax or target-runtime questions.',
|
|
307
|
+
outputSchema: {
|
|
308
|
+
sections: z.array(
|
|
309
|
+
z.object({
|
|
310
|
+
title: z.string(),
|
|
311
|
+
slug: z.string(),
|
|
312
|
+
use_cases: z.string(),
|
|
313
|
+
}),
|
|
314
|
+
),
|
|
315
|
+
},
|
|
316
|
+
annotations: {
|
|
317
|
+
readOnlyHint: true,
|
|
318
|
+
destructiveHint: false,
|
|
319
|
+
openWorldHint: false,
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
async () => {
|
|
323
|
+
const output = list_sections_handler();
|
|
324
|
+
return {
|
|
325
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
326
|
+
structuredContent: output,
|
|
327
|
+
};
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
server.registerTool(
|
|
332
|
+
'get-documentation',
|
|
333
|
+
{
|
|
334
|
+
title: 'Get TSRX Documentation',
|
|
335
|
+
description:
|
|
336
|
+
'Retrieves TSRX documentation for one or more section slugs or titles. Pass a string, comma-separated string, JSON array string, or string array.',
|
|
337
|
+
inputSchema: {
|
|
338
|
+
section: z.union([z.string(), z.array(z.string())]),
|
|
339
|
+
},
|
|
340
|
+
outputSchema: {
|
|
341
|
+
sections: z.array(
|
|
342
|
+
z.object({
|
|
343
|
+
title: z.string(),
|
|
344
|
+
slug: z.string(),
|
|
345
|
+
content: z.string(),
|
|
346
|
+
}),
|
|
347
|
+
),
|
|
348
|
+
missing: z.array(
|
|
349
|
+
z.object({
|
|
350
|
+
section: z.string(),
|
|
351
|
+
similar: z.array(
|
|
352
|
+
z.object({
|
|
353
|
+
title: z.string(),
|
|
354
|
+
slug: z.string(),
|
|
355
|
+
}),
|
|
356
|
+
),
|
|
357
|
+
}),
|
|
358
|
+
),
|
|
359
|
+
},
|
|
360
|
+
annotations: {
|
|
361
|
+
readOnlyHint: true,
|
|
362
|
+
destructiveHint: false,
|
|
363
|
+
openWorldHint: false,
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
async ({ section }) => {
|
|
367
|
+
const output = get_documentation_handler({ section });
|
|
368
|
+
return {
|
|
369
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
370
|
+
structuredContent: output,
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
for (const section of list_documentation_sections()) {
|
|
376
|
+
const uri = `tsrx://docs/${section.slug}.md`;
|
|
377
|
+
server.registerResource(
|
|
378
|
+
`tsrx-docs-${section.slug}`,
|
|
379
|
+
uri,
|
|
380
|
+
{
|
|
381
|
+
title: section.title,
|
|
382
|
+
description: section.use_cases,
|
|
383
|
+
mimeType: 'text/markdown',
|
|
384
|
+
},
|
|
385
|
+
async () => text_resource(uri, section.content),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
for (const [target, content] of Object.entries(TARGET_RESOURCE_CONTENT)) {
|
|
390
|
+
const uri = `tsrx://targets/${target}.md`;
|
|
391
|
+
server.registerResource(
|
|
392
|
+
`tsrx-target-${target}`,
|
|
393
|
+
uri,
|
|
394
|
+
{
|
|
395
|
+
title: `TSRX ${target[0].toUpperCase()}${target.slice(1)} Target`,
|
|
396
|
+
description: `${target} target handoff guidance for TSRX agents`,
|
|
397
|
+
mimeType: 'text/markdown',
|
|
398
|
+
},
|
|
399
|
+
async () => text_resource(uri, content),
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
server.registerPrompt(
|
|
404
|
+
'tsrx-task',
|
|
405
|
+
{
|
|
406
|
+
title: 'TSRX Task Workflow',
|
|
407
|
+
description:
|
|
408
|
+
'Guide an agent through target-aware TSRX work: detect target, fetch docs, compile, and defer runtime-specific details to target layers.',
|
|
409
|
+
argsSchema: {
|
|
410
|
+
task: z.string().optional(),
|
|
411
|
+
target: z.enum(['ripple', 'react', 'preact', 'solid', 'vue']).optional(),
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
async ({ task, target }) => {
|
|
415
|
+
const context = [task ? `Task: ${task}` : null, target ? `Known target: ${target}` : null]
|
|
416
|
+
.filter(Boolean)
|
|
417
|
+
.join('\n');
|
|
418
|
+
const text = context
|
|
419
|
+
? `${context}\n\n${create_tsrx_task_prompt({ remote })}`
|
|
420
|
+
: create_tsrx_task_prompt({ remote });
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
description: 'Target-aware TSRX agent workflow',
|
|
424
|
+
messages: [
|
|
425
|
+
{
|
|
426
|
+
role: 'user',
|
|
427
|
+
content: {
|
|
428
|
+
type: 'text',
|
|
429
|
+
text,
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
],
|
|
433
|
+
};
|
|
434
|
+
},
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
if (!remote) {
|
|
438
|
+
server.registerTool(
|
|
439
|
+
'detect-target',
|
|
440
|
+
{
|
|
441
|
+
title: 'Detect TSRX Runtime Target',
|
|
442
|
+
description:
|
|
443
|
+
'Inspects package.json and common bundler config files to infer whether a project uses TSRX with Ripple, React, Preact, Solid, or Vue.',
|
|
444
|
+
inputSchema: {
|
|
445
|
+
cwd: z.string().optional(),
|
|
446
|
+
},
|
|
447
|
+
outputSchema: {
|
|
448
|
+
...target_detection_schema,
|
|
449
|
+
},
|
|
450
|
+
annotations: {
|
|
451
|
+
readOnlyHint: true,
|
|
452
|
+
destructiveHint: false,
|
|
453
|
+
openWorldHint: false,
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
async ({ cwd }) => {
|
|
457
|
+
const output = detect_target_handler({ cwd });
|
|
458
|
+
return {
|
|
459
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
460
|
+
structuredContent: output,
|
|
461
|
+
};
|
|
462
|
+
},
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
server.registerTool(
|
|
467
|
+
'compile-tsrx',
|
|
468
|
+
{
|
|
469
|
+
title: 'Compile TSRX',
|
|
470
|
+
description:
|
|
471
|
+
'Compiles TSRX code with the inferred or explicit runtime target compiler. Use this to validate generated .tsrx code and collect compiler diagnostics.',
|
|
472
|
+
inputSchema: {
|
|
473
|
+
code: z.string(),
|
|
474
|
+
filename: z.string().optional(),
|
|
475
|
+
target: TARGET_SCHEMA.optional(),
|
|
476
|
+
cwd: z.string().optional(),
|
|
477
|
+
collect: z.boolean().optional(),
|
|
478
|
+
loose: z.boolean().optional(),
|
|
479
|
+
includeCode: z.boolean().optional(),
|
|
480
|
+
mode: z.enum(['client', 'server']).optional(),
|
|
481
|
+
},
|
|
482
|
+
outputSchema: compile_result_schema,
|
|
483
|
+
annotations: {
|
|
484
|
+
readOnlyHint: true,
|
|
485
|
+
destructiveHint: false,
|
|
486
|
+
openWorldHint: false,
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
async (input) => {
|
|
490
|
+
const output = await compile_tsrx_handler(remote ? { ...input, cwd: undefined } : input);
|
|
491
|
+
if (remote) output.cwd = 'remote';
|
|
492
|
+
return {
|
|
493
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
494
|
+
structuredContent: output,
|
|
495
|
+
};
|
|
496
|
+
},
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
server.registerTool(
|
|
500
|
+
'format-tsrx',
|
|
501
|
+
{
|
|
502
|
+
title: 'Format TSRX',
|
|
503
|
+
description:
|
|
504
|
+
'Formats TSRX code using the official @tsrx/prettier-plugin. Use this before returning generated .tsrx code or to check whether source is already formatted.',
|
|
505
|
+
inputSchema: {
|
|
506
|
+
code: z.string(),
|
|
507
|
+
filename: z.string().optional(),
|
|
508
|
+
cwd: z.string().optional(),
|
|
509
|
+
printWidth: z.number().int().positive().optional(),
|
|
510
|
+
tabWidth: z.number().int().positive().optional(),
|
|
511
|
+
useTabs: z.boolean().optional(),
|
|
512
|
+
singleQuote: z.boolean().optional(),
|
|
513
|
+
check: z.boolean().optional(),
|
|
514
|
+
},
|
|
515
|
+
outputSchema: format_result_schema,
|
|
516
|
+
annotations: {
|
|
517
|
+
readOnlyHint: true,
|
|
518
|
+
destructiveHint: false,
|
|
519
|
+
openWorldHint: false,
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
async (input) => {
|
|
523
|
+
const output = await format_tsrx_handler(remote ? { ...input, cwd: undefined } : input);
|
|
524
|
+
if (remote) {
|
|
525
|
+
output.cwd = 'remote';
|
|
526
|
+
output.configPath = null;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
530
|
+
structuredContent: output,
|
|
531
|
+
};
|
|
532
|
+
},
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
server.registerTool(
|
|
536
|
+
'analyze-tsrx',
|
|
537
|
+
{
|
|
538
|
+
title: 'Analyze TSRX Diagnostics',
|
|
539
|
+
description:
|
|
540
|
+
'Compiles TSRX code and turns compiler diagnostics into target-neutral authoring advice with linked TSRX docs resources. Use this before manually guessing fixes.',
|
|
541
|
+
inputSchema: {
|
|
542
|
+
code: z.string(),
|
|
543
|
+
filename: z.string().optional(),
|
|
544
|
+
target: TARGET_SCHEMA.optional(),
|
|
545
|
+
cwd: z.string().optional(),
|
|
546
|
+
collect: z.boolean().optional(),
|
|
547
|
+
loose: z.boolean().optional(),
|
|
548
|
+
mode: z.enum(['client', 'server']).optional(),
|
|
549
|
+
},
|
|
550
|
+
outputSchema: analysis_result_schema,
|
|
551
|
+
annotations: {
|
|
552
|
+
readOnlyHint: true,
|
|
553
|
+
destructiveHint: false,
|
|
554
|
+
openWorldHint: false,
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
async (input) => {
|
|
558
|
+
const output = await analyze_tsrx_handler(remote ? { ...input, cwd: undefined } : input);
|
|
559
|
+
if (remote) output.cwd = 'remote';
|
|
560
|
+
return {
|
|
561
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
562
|
+
structuredContent: output,
|
|
563
|
+
};
|
|
564
|
+
},
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
if (!remote) {
|
|
568
|
+
server.registerTool(
|
|
569
|
+
'inspect-project',
|
|
570
|
+
{
|
|
571
|
+
title: 'Inspect TSRX Project',
|
|
572
|
+
description:
|
|
573
|
+
'Inspects package.json, target signals, TSRX packages, tooling packages, scripts, and common config files for a project. Use this before choosing target-specific advice or repo commands.',
|
|
574
|
+
inputSchema: {
|
|
575
|
+
cwd: z.string().optional(),
|
|
576
|
+
},
|
|
577
|
+
outputSchema: inspect_project_result_schema,
|
|
578
|
+
annotations: {
|
|
579
|
+
readOnlyHint: true,
|
|
580
|
+
destructiveHint: false,
|
|
581
|
+
openWorldHint: false,
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
async ({ cwd }) => {
|
|
585
|
+
const output = inspect_project_handler({ cwd });
|
|
586
|
+
return {
|
|
587
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
588
|
+
structuredContent: output,
|
|
589
|
+
};
|
|
590
|
+
},
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
server.registerTool(
|
|
594
|
+
'validate-tsrx-file',
|
|
595
|
+
{
|
|
596
|
+
title: 'Validate TSRX File',
|
|
597
|
+
description:
|
|
598
|
+
'Reads a .tsrx file and runs the full MCP validation loop: formatting check, target-aware compilation, and diagnostic advice. This tool is read-only and does not rewrite files.',
|
|
599
|
+
inputSchema: {
|
|
600
|
+
filePath: z.string(),
|
|
601
|
+
cwd: z.string().optional(),
|
|
602
|
+
target: TARGET_SCHEMA.optional(),
|
|
603
|
+
collect: z.boolean().optional(),
|
|
604
|
+
loose: z.boolean().optional(),
|
|
605
|
+
mode: z.enum(['client', 'server']).optional(),
|
|
606
|
+
printWidth: z.number().int().positive().optional(),
|
|
607
|
+
tabWidth: z.number().int().positive().optional(),
|
|
608
|
+
useTabs: z.boolean().optional(),
|
|
609
|
+
singleQuote: z.boolean().optional(),
|
|
610
|
+
},
|
|
611
|
+
outputSchema: validate_file_result_schema,
|
|
612
|
+
annotations: {
|
|
613
|
+
readOnlyHint: true,
|
|
614
|
+
destructiveHint: false,
|
|
615
|
+
openWorldHint: false,
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
async (input) => {
|
|
619
|
+
const output = await validate_tsrx_file_handler(input);
|
|
620
|
+
return {
|
|
621
|
+
content: [{ type: 'text', text: json_text(output) }],
|
|
622
|
+
structuredContent: output,
|
|
623
|
+
};
|
|
624
|
+
},
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return server;
|
|
629
|
+
}
|
package/src/stdio.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createTSRXMcpServer } from './server.js';
|
|
4
|
+
|
|
5
|
+
const server = createTSRXMcpServer();
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
|
|
8
|
+
await server.connect(transport);
|