@tsrx/mcp 0.0.33 → 0.0.35
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 +1 -1
- package/package.json +3 -3
- package/src/analyze.js +31 -8
- package/src/authoring.js +6 -5
- package/src/generated/docs.js +8 -7
- package/src/server.js +4 -3
- package/src/target.js +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ Add the generic config above to your Codex MCP configuration.
|
|
|
88
88
|
target-neutral authoring advice with linked docs resources.
|
|
89
89
|
- `review-tsrx-accessibility` - review TSRX source for common accessibility issues
|
|
90
90
|
before browser-based Axe validation, including missing button names, unlabeled
|
|
91
|
-
form controls, and visible text
|
|
91
|
+
form controls, and visible text accidentally wrapped in quote characters.
|
|
92
92
|
- `review-tsrx-styles` - review function-local style usage for malformed style
|
|
93
93
|
blocks, broad selectors, root styling, and contrast risks.
|
|
94
94
|
- `review-tsrx-components` - review component structure and suggest extraction
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "MCP server for TSRX documentation and project context",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.35",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
38
|
"prettier": "^3.8.3",
|
|
39
39
|
"zod": "^4.3.6",
|
|
40
|
-
"@tsrx/core": "0.1.
|
|
41
|
-
"@tsrx/prettier-plugin": "0.3.
|
|
40
|
+
"@tsrx/core": "0.1.22",
|
|
41
|
+
"@tsrx/prettier-plugin": "0.3.74"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^24.3.0",
|
package/src/analyze.js
CHANGED
|
@@ -3,7 +3,11 @@ import {
|
|
|
3
3
|
TSRX_DO_WHILE_STATEMENT_ERROR,
|
|
4
4
|
TSRX_FOR_IN_STATEMENT_ERROR,
|
|
5
5
|
TSRX_FOR_STATEMENT_ERROR,
|
|
6
|
+
TSRX_IF_BREAK_ERROR,
|
|
7
|
+
TSRX_IF_CONTINUE_ERROR,
|
|
8
|
+
TSRX_IF_RETURN_ERROR,
|
|
6
9
|
TSRX_LOOP_BREAK_ERROR,
|
|
10
|
+
TSRX_LOOP_CONTINUE_ERROR,
|
|
7
11
|
TSRX_LOOP_RETURN_ERROR,
|
|
8
12
|
TSRX_RETURN_STATEMENT_ERROR,
|
|
9
13
|
TSRX_WHILE_STATEMENT_ERROR,
|
|
@@ -76,20 +80,39 @@ function create_advice(input) {
|
|
|
76
80
|
advice.push({
|
|
77
81
|
kind: 'jsx-expression-value',
|
|
78
82
|
severity: 'info',
|
|
79
|
-
title: '
|
|
83
|
+
title: 'Use JSX-shaped expression values',
|
|
80
84
|
message:
|
|
81
|
-
'
|
|
82
|
-
documentation: ['tsrx://docs/
|
|
85
|
+
'TSRX expression values use JSX-shaped nodes. Use a JSXElement directly for one child, a JSXFragment when the value needs multiple children, or a JSX statement container when setup must produce one final output.',
|
|
86
|
+
documentation: ['tsrx://docs/expression-values.md'],
|
|
83
87
|
});
|
|
84
88
|
}
|
|
85
89
|
|
|
86
|
-
if (
|
|
90
|
+
if (
|
|
91
|
+
error_messages.has(TSRX_LOOP_RETURN_ERROR) ||
|
|
92
|
+
error_messages.has(TSRX_LOOP_BREAK_ERROR) ||
|
|
93
|
+
error_messages.has(TSRX_LOOP_CONTINUE_ERROR)
|
|
94
|
+
) {
|
|
87
95
|
advice.push({
|
|
88
96
|
kind: 'tsrx-loop-control-flow',
|
|
89
97
|
severity: 'error',
|
|
90
|
-
title: '
|
|
98
|
+
title: 'Filter before TSRX for...of loops',
|
|
99
|
+
message:
|
|
100
|
+
'Direct continue, break, and return statements are not valid inside TSRX @for loops. Filter the iterable before rendering, use { ... } for the no-items fallback, and keep ordinary JavaScript control flow inside nested functions.',
|
|
101
|
+
documentation: ['tsrx://docs/control-flow.md'],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (
|
|
106
|
+
error_messages.has(TSRX_IF_RETURN_ERROR) ||
|
|
107
|
+
error_messages.has(TSRX_IF_BREAK_ERROR) ||
|
|
108
|
+
error_messages.has(TSRX_IF_CONTINUE_ERROR)
|
|
109
|
+
) {
|
|
110
|
+
advice.push({
|
|
111
|
+
kind: 'tsrx-if-control-flow',
|
|
112
|
+
severity: 'error',
|
|
113
|
+
title: 'Keep @if branches render-only',
|
|
91
114
|
message:
|
|
92
|
-
'
|
|
115
|
+
'Direct return, continue, and break statements are not valid inside TSRX @if branches. Use an ordinary JavaScript if statement before template output for guard returns, or render the branch output conditionally.',
|
|
93
116
|
documentation: ['tsrx://docs/control-flow.md'],
|
|
94
117
|
});
|
|
95
118
|
}
|
|
@@ -98,9 +121,9 @@ function create_advice(input) {
|
|
|
98
121
|
advice.push({
|
|
99
122
|
kind: 'tsrx-template-return',
|
|
100
123
|
severity: 'error',
|
|
101
|
-
title: '
|
|
124
|
+
title: 'Put returns in TypeScript setup',
|
|
102
125
|
message:
|
|
103
|
-
'Return statements are ordinary JavaScript control flow for functions, not template
|
|
126
|
+
'Return statements are ordinary JavaScript control flow for functions, not template output. Use guard clauses before a JSX statement container or return value, or render conditionally inside the template.',
|
|
104
127
|
documentation: ['tsrx://docs/control-flow.md'],
|
|
105
128
|
});
|
|
106
129
|
}
|
package/src/authoring.js
CHANGED
|
@@ -129,11 +129,11 @@ function collect_direct_quoted_text_issues(code) {
|
|
|
129
129
|
issues.push({
|
|
130
130
|
kind: 'direct-quoted-text',
|
|
131
131
|
severity: match[1] === 'button' ? 'error' : 'warning',
|
|
132
|
-
title: '
|
|
132
|
+
title: 'Write visible TSRX text without extra quotes',
|
|
133
133
|
message:
|
|
134
|
-
'
|
|
134
|
+
'Quoted text inside TSRX elements renders the quote characters as part of JSX text. Use plain JSX text unless you intentionally want visible quotes.',
|
|
135
135
|
snippet: line_snippet(match[0]),
|
|
136
|
-
recommendation: `Replace it with
|
|
136
|
+
recommendation: `Replace it with plain JSX text, for example <${match[1]}>${match[3]}</${match[1]}>.`,
|
|
137
137
|
documentation: ['tsrx://docs/text-and-template-expressions.md'],
|
|
138
138
|
});
|
|
139
139
|
}
|
|
@@ -160,6 +160,7 @@ export function review_tsrx_accessibility(input) {
|
|
|
160
160
|
has_attribute(attrs, 'aria-label') ||
|
|
161
161
|
has_attribute(attrs, 'aria-labelledby') ||
|
|
162
162
|
has_attribute(attrs, 'title') ||
|
|
163
|
+
has_visible_label_text(inner) ||
|
|
163
164
|
(has_expression_text(inner) && !/^\s*\{\s*['"`]\s*['"`]\s*\}\s*$/.test(inner));
|
|
164
165
|
|
|
165
166
|
if (!has_name || has_direct_quoted_text(inner)) {
|
|
@@ -171,7 +172,7 @@ export function review_tsrx_accessibility(input) {
|
|
|
171
172
|
'Buttons must expose a visible label or an aria-label. This is especially important for disabled submit buttons and icon-only controls.',
|
|
172
173
|
snippet: line_snippet(match[0]),
|
|
173
174
|
recommendation:
|
|
174
|
-
|
|
175
|
+
'Use visible JSX text such as Add task or add aria-label for icon-only buttons.',
|
|
175
176
|
documentation: ['tsrx://docs/text-and-template-expressions.md'],
|
|
176
177
|
});
|
|
177
178
|
}
|
|
@@ -227,7 +228,7 @@ export function review_tsrx_accessibility(input) {
|
|
|
227
228
|
? ['Run browser-based Axe or the benchmark validation loop.']
|
|
228
229
|
: [
|
|
229
230
|
'Fix error-severity issues before returning code.',
|
|
230
|
-
'Prefer
|
|
231
|
+
'Prefer plain JSX text for visible labels.',
|
|
231
232
|
'Run review-tsrx-accessibility again, then compile-tsrx and browser-based Axe.',
|
|
232
233
|
],
|
|
233
234
|
};
|
package/src/generated/docs.js
CHANGED
|
@@ -11,7 +11,7 @@ export const documentation_sections = [
|
|
|
11
11
|
use_cases:
|
|
12
12
|
'always, introduction, explain tsrx, compare jsx, language model context, runtime targets',
|
|
13
13
|
content:
|
|
14
|
-
'# TSRX Overview\n\nTSRX is a TypeScript language extension for authoring declarative UI in .tsrx files. It adds a small set of syntax forms on top of TypeScript, while letting each target compiler define the runtime semantics.\n\nCore ideas:\n- Components are ordinary TypeScript functions
|
|
14
|
+
'# TSRX Overview\n\nTSRX is a TypeScript language extension for authoring declarative UI in .tsrx files. It adds a small set of syntax forms on top of TypeScript, while letting each target compiler define the runtime semantics.\n\nCore ideas:\n- Components are ordinary TypeScript functions. Use a JSX statement container, `@{ ... }`, when the function body is mostly TSRX setup plus one rendered output.\n- JSXElement, JSXFragment, JSXText, JSXExpressionContainer, attributes, and spreads use the standard JSX node family.\n- A mixed setup/template scope must finish with exactly one output node: a JSXElement, JSXFragment, or JSX control-flow expression. Wrap plain text, expression containers, or multiple siblings in a fragment.\n- Template control flow uses directive expressions: `@if`, `@for`, `@switch`, and `@try`; every directive body uses a `{...}` template block.\n- lazy destructuring uses &[] and &{} for by-reference bindings.\n\nThe core language docs should stay target-neutral. After identifying the active runtime target, use target-specific docs, prompts, or skills for runtime imports, bundler setup, and semantics that are not defined by TSRX itself.\n\nSource: website-tsrx/src/pages/specification.tsrx',
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
slug: 'components',
|
|
@@ -19,14 +19,15 @@ export const documentation_sections = [
|
|
|
19
19
|
use_cases:
|
|
20
20
|
'components, functions, props, authoring .tsrx files, jsx return syntax',
|
|
21
21
|
content:
|
|
22
|
-
'# Function Components\n\nAuthor UI as ordinary TypeScript functions
|
|
22
|
+
'# Function Components\n\nAuthor UI as ordinary TypeScript functions. Components can return JSX directly, or use a JSX statement container when setup and output should live together.\n\n```tsx\nexport function Button({ label }: { label: string }) @{\n <button>{label}</button>\n}\n```\n\nInside `@{ ... }`, put any setup statements first and end with one rendered output node. No JavaScript setup can appear after that output.\n\nSource: website-tsrx/src/pages/specification.tsrx#components',
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
slug: 'text-and-template-expressions',
|
|
26
26
|
title: 'Text and Template Expressions',
|
|
27
|
-
use_cases:
|
|
27
|
+
use_cases:
|
|
28
|
+
'text children, jsx text, comments, string literals, expression containers',
|
|
28
29
|
content:
|
|
29
|
-
'# Text and Template Expressions\n\
|
|
30
|
+
'# Text and Template Expressions\n\nStatic text is JSXText and can be written directly between tags. Dynamic values use normal JSX expression containers.\n\n```tsx\nfunction Greeting({ name }: { name: string }) @{\n <>\n <h1>Hello</h1>\n <p>{name}</p>\n </>\n}\n```\n\nJavaScript comments are also allowed between template children and are not rendered. Use braces for JavaScript expressions, including string literals that should be evaluated as JavaScript.\n\nSpecification grammar:\n\n```text\nJSXTextChild :\n JSXTextCharacters\n\nJSXExpressionContainer :\n { AssignmentExpression }\n\nJSXCodeBlock :\n @{ StatementListItemListopt TemplateOutput }\n\nTemplateBlock :\n { TemplateChildrenopt }\n { StatementListItemList TemplateOutput }\n\nJSXIfExpression :\n @if ( Expression ) TemplateBlock\n @if ( Expression ) TemplateBlock else TemplateBlock\n\nJSXForExpression :\n @for ( ForHeader TemplateForOptionsopt ) TemplateBlock\n @for ( ForHeader TemplateForOptionsopt ) TemplateBlock empty TemplateBlock\n\nJSXSwitchExpression :\n @switch ( Expression ) { JSXSwitchCaseListopt }\n\nJSXSwitchCase :\n case Expression : TemplateBlock\n default : TemplateBlock\n\nJSXTryExpression :\n @try TemplateBlock pending TemplateBlock\n @try TemplateBlock catch ( CatchParameteropt ) TemplateBlock\n @try TemplateBlock finally TemplateBlock\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#templates',
|
|
30
31
|
},
|
|
31
32
|
{
|
|
32
33
|
slug: 'expression-values',
|
|
@@ -34,7 +35,7 @@ export const documentation_sections = [
|
|
|
34
35
|
use_cases:
|
|
35
36
|
'fragments, pass template as prop, return template from helper, render props, expression position jsx',
|
|
36
37
|
content:
|
|
37
|
-
'# Expression Values\n\
|
|
38
|
+
'# Expression Values\n\nTSRX uses JSX-shaped expression values. A single JSXElement can be assigned, passed, or returned directly. Use a JSXFragment when the value needs multiple children, or use a JSX statement container when setup needs to produce one final output.\n\n```tsx\nfunction App() @{\n const title = <span class="title">Settings</span>;\n const badge = (label: string) => @{\n const normalized = label.trim();\n\n <span class="badge">{normalized}</span>\n };\n\n <Card title={title}>{badge(\'New\')}</Card>\n}\n```\n\n`@{ ... }` is a JSX statement container. A normal JSX fragment, element body, or control-flow branch can also contain setup before output, but that scope must still end with one output node. Use a fragment when the output is text, an expression container, or multiple siblings.\n\nSpecification grammar:\n\n```text\nPrimaryExpression :\n JSXElement\n JSXFragment\n JSXStyleElement\n JSXCodeBlock\n JSXIfExpression\n JSXForExpression\n JSXSwitchExpression\n JSXTryExpression\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#expression-values',
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
slug: 'control-flow',
|
|
@@ -42,7 +43,7 @@ export const documentation_sections = [
|
|
|
42
43
|
use_cases:
|
|
43
44
|
'if else, for loops, switch, try catch, conditional rendering, lists, guard returns',
|
|
44
45
|
content:
|
|
45
|
-
'# Control Flow\n\
|
|
46
|
+
'# Control Flow\n\nTemplate control flow uses directive-prefixed expressions inside JSX children.\n\n```tsx\nfunction List({ items }: { items: string[] }) @{\n <>\n @if (items.length === 0) {\n <p>No items</p>\n } @else {\n <ul>\n @for (const item of items.filter(Boolean); index i; key item) {\n <li>{item}</li>\n } @empty {\n <li>No items</li>\n }\n </ul>\n }\n </>\n}\n```\n\nUse normal function returns for guard exits before entering template output. Filter a collection before passing it to `@for` when some items should not render, and use ` { ... }` for the no-items fallback.\n\n`return` statements are not template output. Put guard returns before the JSX statement container or return value, or render conditionally with `@if`. Inside TSRX `@if` branches and `@for ... of` loops, direct `continue`, `break`, and `return` statements are invalid. Inside a TSRX `@switch` case body, both `break` and `return` are invalid because cases are isolated template blocks.\n\nTSRX rendering supports `@for ... of` list loops. Regular `for`, `for...in`, `while`, and `do...while` loops are not rendering constructs. Move imperative loops into setup code, a nested function, event handler, effect, or helper where normal JavaScript control-flow rules apply.\n\nSource: website-tsrx/src/pages/features.tsrx#for',
|
|
46
47
|
},
|
|
47
48
|
{
|
|
48
49
|
slug: 'lazy-destructuring',
|
|
@@ -57,7 +58,7 @@ export const documentation_sections = [
|
|
|
57
58
|
use_cases:
|
|
58
59
|
'style expressions, scoped css, module server, submodule imports, compile-time identifiers',
|
|
59
60
|
content:
|
|
60
|
-
'# Style and Server Extensions\n\nAssign a `<style>` expression to expose scoped CSS class names declared in the current module.\n\n```tsx\nconst styles = <style>\n .card { padding: 1rem; }\n</style>;\n\
|
|
61
|
+
'# Style and Server Extensions\n\nAssign a `<style>` expression to expose scoped CSS class names declared in the current module.\n\n```tsx\nconst styles = <style>\n .card { padding: 1rem; }\n</style>;\n\nexport function ChildCard() @{\n <>\n <Child class={styles.card} />\n </>\n}\n```\n\n`module server { ... }` declares a server-oriented submodule in the Ripple host profile. Import exported functions with `import { load } from server` before use.\n\nSpecification grammar:\n\n```text\nJSXStyleElement :\n <style JSXAttributesopt> CSSSource </style>\n\nSubmoduleDeclaration :\n module Identifier { ModuleItemListopt }\n\nSubmoduleImportDeclaration :\n import ImportClause from Identifier ;\n\n```\n\nSource: website-tsrx/src/pages/specification.tsrx#style',
|
|
61
62
|
},
|
|
62
63
|
{
|
|
63
64
|
slug: 'target-integration',
|
package/src/server.js
CHANGED
|
@@ -305,9 +305,10 @@ function create_tsrx_task_prompt(options) {
|
|
|
305
305
|
1. Identify whether the task is about target-neutral TSRX syntax, target runtime behavior, or both.
|
|
306
306
|
${project_context_step}
|
|
307
307
|
3. For syntax uncertainty, use \`list-sections\`, \`get-documentation\`, or read \`tsrx://docs/{slug}.md\`.
|
|
308
|
-
4. Keep core TSRX advice target-neutral: component functions,
|
|
308
|
+
4. Keep core TSRX advice target-neutral: component functions, JSX expression values, JSX statement containers \`@{ ... }\`, JSX text, directive control flow, lazy destructuring, style identifiers, and submodule declarations.
|
|
309
309
|
5. Use \`tsrx://targets/{target}.md\` as the handoff point for target-specific responsibilities.
|
|
310
|
-
5a. In
|
|
310
|
+
5a. In template output, render lists with \`@for (... of ...)\`; filter the iterable before rendering when items should be skipped, and use \` { ... }\` for the no-items fallback; use \`@if\`, \`@switch\`, and \`@try\` for rendering control flow. Every directive body uses a \`{...}\` template block. When a scope mixes setup with output, setup comes first and the scope ends with exactly one JSX element, JSX fragment, or JSX control-flow expression.
|
|
311
|
+
5b. \`return\` is JavaScript function control flow, not template output. Use guard returns before a JSX statement container or return value, or render conditionally with \`@if\`. Do not use \`continue\`, \`break\`, or \`return\` inside \`@if\` template branches or \`@for\` template loops. \`@switch\` cases are isolated blocks and do not use \`break\` or \`return\`.
|
|
311
312
|
${file_validation_step}
|
|
312
313
|
${compile_step}
|
|
313
314
|
${authoring_step}
|
|
@@ -600,7 +601,7 @@ export function createTSRXMcpServer(options = {}) {
|
|
|
600
601
|
{
|
|
601
602
|
title: 'Review TSRX Accessibility',
|
|
602
603
|
description:
|
|
603
|
-
'Reviews TSRX source for common accessibility issues before browser-based Axe validation, including missing button names, unlabeled form controls, and
|
|
604
|
+
'Reviews TSRX source for common accessibility issues before browser-based Axe validation, including missing button names, unlabeled form controls, and visible text accidentally wrapped in quote characters.',
|
|
604
605
|
inputSchema: {
|
|
605
606
|
code: z.string(),
|
|
606
607
|
filename: z.string().optional(),
|
package/src/target.js
CHANGED
|
@@ -5,7 +5,7 @@ export const TARGET_CANDIDATES = [
|
|
|
5
5
|
{
|
|
6
6
|
target: 'ripple',
|
|
7
7
|
compilerPackage: '@tsrx/ripple',
|
|
8
|
-
signals: ['@tsrx/ripple', 'ripple', '@ripple-ts/vite-plugin'
|
|
8
|
+
signals: ['@tsrx/ripple', 'ripple', '@ripple-ts/vite-plugin'],
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
target: 'react',
|