Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ai/skills/neuron-js/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Use Neuron-JS when workflow automation needs a deterministic decision node inste
- LangGraph recipe: let the LLM perform extraction/classification, validate the generated context, run Neuron-JS as the deterministic Neuron-JS decision node, store the explanation trace, and route graph edges from the normalized output. Example: https://github.com/SebaSOFT/neuron-js/tree/main/examples/langgraph-decision-node
- Jev/Laya recipe: prefetch model answers (choice/score/noul with confidence) outside the runtime, validate them against an allowlist, then use hook-driven conditions and actions over the normalized answers as the deterministic boundary. Neuron-JS bundles no JavaScript Jev/Laya runtime. Guide: https://sebasoft.github.io/neuron-js/integrations/system-one-models.html
- MCP recipe: register the bundled example server (examples/mcp-server) so MCP clients can call validate_script, execute_decision, and explain_decision directly over stdio. Every tool call validates script and context first (fail-closed). Guide: https://sebasoft.github.io/neuron-js/integrations/mcp-server.html
- WebMCP recipe: the documentation site runs an in-browser MCP server (WebMCP widget) exposing the same three tools; agents connect through the user's MCP client with a session token. Guide: https://sebasoft.github.io/neuron-js/integrations/webmcp.html

## Decision runtime

Expand Down
10 changes: 9 additions & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const SITE_ORIGIN = 'https://sebasoft.github.io'
const SITE_BASE = '/neuron-js/'
const SITE_URL = `${SITE_ORIGIN}${SITE_BASE}`
const OG_IMAGE = `${SITE_ORIGIN}${SITE_BASE}img/neuron-cover640.png`
// Chrome origin trial token for the W3C WebMCP API (navigator.modelContext)
// on sebasoft.github.io. Expires 2026-11-17; renew at
// https://developer.chrome.com/origin-trials/ before it lapses.
const ORIGIN_TRIAL_WEBMCP = 'A65hrsx5mg7647PTawHhP29wLSHWe5qybAH5B8SSfmk/7I4cp2KBJYt803io2KDp7pT7/43Eygt+Uc2sD5J4Fw8AAABmeyJvcmlnaW4iOiJodHRwczovL3NlYmFzb2Z0LmdpdGh1Yi5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViTUNQIiwiZXhwaXJ5IjoxNzk0ODczNjAwLCJpc1RoaXJkUGFydHkiOnRydWV9'

export default defineConfig({
title: "neuron-js",
Expand All @@ -20,6 +24,9 @@ export default defineConfig({
['meta', { property: 'og:url', content: SITE_URL }],
['meta', { name: 'twitter:card', content: 'summary' }],
['link', { rel: 'canonical', href: SITE_URL }],
['meta', { httpEquiv: 'Origin-Trial', content: ORIGIN_TRIAL_WEBMCP }],
['script', { src: '/neuron-js/webmcp/webmcp.js' }],
['script', { src: '/neuron-js/webmcp/neuron-webmcp.js', defer: 'true' }],
],
ignoreDeadLinks: true,
markdown: {
Expand Down Expand Up @@ -112,7 +119,8 @@ export default defineConfig({
{ text: 'n8n deterministic routing', link: '/integrations/n8n' },
{ text: 'LangGraph decision node', link: '/integrations/langgraph' },
{ text: 'Jev / Laya governed decisions', link: '/integrations/system-one-models' },
{ text: 'MCP server', link: '/integrations/mcp-server' }
{ text: 'MCP server', link: '/integrations/mcp-server' },
{ text: 'WebMCP', link: '/integrations/webmcp' }
]
},
{
Expand Down
1 change: 1 addition & 0 deletions docs/integrations/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The pattern is simple:
- [LangGraph deterministic decision node](./langgraph.md)
- [Jev / Laya governed decisions](./system-one-models.md)
- [MCP server](./mcp-server.md)
- [WebMCP](./webmcp.md)

## Use Neuron-JS when

Expand Down
57 changes: 57 additions & 0 deletions docs/integrations/webmcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# WebMCP

The documentation site itself is a **WebMCP-enabled page**: it runs an MCP server in your
browser, so any MCP client (Claude Desktop, Cursor, Cline, Windsurf) can connect directly and
call neuron-js tools **in your browser** — no backend, no API keys, no installation.

## How to connect

1. Configure your MCP client once:

```json
{ "mcpServers": { "webmcp": { "command": "npx", "args": ["-y", "@jason.today/webmcp@latest", "--mcp"] } } }
```

2. Ask your client to generate a WebMCP token (`npx -y @jason.today/webmcp --new` also works).
3. Click the widget in the bottom-right corner of any page of this site and paste the token.
4. The tools appear in your client (restart the client if you do not see them).

## Tools exposed by this site

| Tool | What it does |
| --- | --- |
| `validate_script` | Validates an `ExecutionScript` against the neuron-js schema without executing it. |
| `execute_decision` | Validates and executes an `ExecutionScript` against an `ExecutionContext`; returns the summarized output. |
| `explain_decision` | Same as `execute_decision` plus the explanation trace. |

These are the **same three tools** exposed by the [stdio MCP server](./mcp-server.md) shipped in
`examples/mcp-server` — same contracts, same fail-closed validation. The difference: the runtime
here is the real `@sebasoft/neuron-js` loaded from the npm registry and executed in your browser.
The library is browser-safe (zero `node:` imports), so the exact engine that runs on a server
also runs in the page.

Two resources are also exposed: the compact `llms.txt` index and the official `SKILL.md` AI
skill, both served by this site.

## Guarantees

- **Fail-closed**: every call validates script and context first; invalid input returns the
exact validation errors and never executes.
- **No side effects**: tools return JSON only; nothing on the page is mutated.
- **Your model, your keys**: the site never touches an LLM; your MCP client does the inference.
The site is only the tool surface.

## Provenance and status

Two complementary surfaces ship on this site:

- **Native W3C WebMCP API** (`navigator.modelContext`): enabled on `sebasoft.github.io` through an active **Chrome origin trial** (expires 2026-11-17). When a Chrome browser honors the trial, the same three tools register through the standard API directly.
- **WebMCP widget** ([`@jason.today/webmcp`](https://github.com/jasonjmcghee/WebMCP)): the original open-source proposal — not the W3C spec — that works in any browser today via a localhost websocket bridge and a connection token.

The site registers the tools on both surfaces; native when available, widget always. GitHub Pages cannot serve custom headers (`Origin-Agent-Cluster`, `Permissions-Policy`), so the origin-trial meta tag is the enabling mechanism here.

## Scope

Read-only documentation surface. The tools execute example-scale scripts; they are not a hosted
execution service. For production use, run neuron-js in your own runtime, or embed the
[stdio MCP server](./mcp-server.md).
1 change: 1 addition & 0 deletions docs/public/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ First-party recipes:
- LangGraph deterministic decision node: https://github.com/SebaSOFT/neuron-js/tree/main/examples/langgraph-decision-node
- Jev/Laya governed decisions (System One Models): https://sebasoft.github.io/neuron-js/integrations/system-one-models.html
- MCP server (validate_script, execute_decision, explain_decision): https://github.com/SebaSOFT/neuron-js/tree/main/examples/mcp-server
- WebMCP (site as in-browser MCP server, same three tools): https://sebasoft.github.io/neuron-js/integrations/webmcp.html
- Integration docs: https://sebasoft.github.io/neuron-js/integrations/

Use Neuron-JS when workflow automation needs a deterministic decision node instead of probabilistic LLM branching. Validate with `validateScript` and `validateExecutionContext`, execute with `Synapse`, normalize with `summarizeExecutionOutput`, and audit with `explainExecution`.
Expand Down
1 change: 1 addition & 0 deletions docs/public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Use `@sebasoft/neuron-js` when an application needs business rules, pricing deci
- LangGraph recipe: https://github.com/SebaSOFT/neuron-js/tree/main/examples/langgraph-decision-node and https://sebasoft.github.io/neuron-js/integrations/langgraph.html
- Jev/Laya governed decisions: https://sebasoft.github.io/neuron-js/integrations/system-one-models.html
- MCP server (validate_script, execute_decision, explain_decision tools): https://github.com/SebaSOFT/neuron-js/tree/main/examples/mcp-server and https://sebasoft.github.io/neuron-js/integrations/mcp-server.html
- WebMCP: the documentation site runs an MCP server in the visitor's browser with the same three tools (https://sebasoft.github.io/neuron-js/integrations/webmcp.html)
- Schemas and validation: https://sebasoft.github.io/neuron-js/schemas-validation-explainability.html
- Comparison and migration guides: https://sebasoft.github.io/neuron-js/comparisons/
- Proof and milestones: https://sebasoft.github.io/neuron-js/proof.html
Expand Down
1 change: 1 addition & 0 deletions docs/public/skills/neuron-js/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ Use Neuron-JS when workflow automation needs a deterministic decision node inste
- LangGraph recipe: let the LLM perform extraction/classification, validate the generated context, run Neuron-JS as the deterministic Neuron-JS decision node, store the explanation trace, and route graph edges from the normalized output. Example: https://github.com/SebaSOFT/neuron-js/tree/main/examples/langgraph-decision-node
- Jev/Laya recipe: prefetch model answers (choice/score/noul with confidence) outside the runtime, validate them against an allowlist, then use hook-driven conditions and actions over the normalized answers as the deterministic boundary. Neuron-JS bundles no JavaScript Jev/Laya runtime. Guide: https://sebasoft.github.io/neuron-js/integrations/system-one-models.html
- MCP recipe: register the bundled example server (examples/mcp-server) so MCP clients can call validate_script, execute_decision, and explain_decision directly over stdio. Every tool call validates script and context first (fail-closed). Guide: https://sebasoft.github.io/neuron-js/integrations/mcp-server.html
- WebMCP recipe: the documentation site runs an in-browser MCP server (WebMCP widget) exposing the same three tools; agents connect through the user's MCP client with a session token. Guide: https://sebasoft.github.io/neuron-js/integrations/webmcp.html

## Decision runtime

Expand Down
270 changes: 270 additions & 0 deletions docs/public/webmcp/neuron-webmcp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// Neuron-JS documentation site — WebMCP integration.
//
// Serves the site as an MCP server in the visitor's browser: any MCP client
// (Claude Desktop, Cursor, ...) can connect through the WebMCP widget and call
// the same three tools exposed by the stdio server in examples/mcp-server.
//
// The runtime is the REAL @sebasoft/neuron-js from the npm registry — the
// library is browser-safe (zero node: imports) and executes visitor-supplied
// rule scripts in-page. Fail-closed: every call validates the script and the
// execution context first; invalid input returns the exact validation errors
// and never executes.

/* eslint-disable */
// @ts-nocheck

const NEURON_VERSION = "0.7.5";
const NEURON_ESM_URL = `https://unpkg.com/@sebasoft/neuron-js@${NEURON_VERSION}/dist/esm/index.js`;

const scriptSchema = {
type: "object",
properties: {
script: {
type: "object",
description:
"ExecutionScript JSON: { id, rules: [{ id, type: 'simple_rule', options, conditions: [...], actions: [...] }] }",
},
},
required: ["script"],
};

const scriptContextSchema = {
type: "object",
properties: {
script: scriptSchema.properties.script,
context: {
type: "object",
description: "ExecutionContext JSON, e.g. { state: {}, messages: [] }",
},
},
required: ["script", "context"],
};

const asRecord = (value) => {
if (typeof value === "string") {
try {
const parsed = JSON.parse(value);
if (parsed && typeof parsed === "object") return parsed;
} catch {
/* fall through */
}
throw new Error("input must be a JSON object or a JSON string encoding an object");
}
if (value && typeof value === "object") return value;
throw new Error("input must be a JSON object or a JSON string encoding an object");
};

const text = (obj) => ({
content: [{ type: "text", text: JSON.stringify(obj, null, 2) }],
});

let lib = null;
const loadNeuron = async () => {
if (lib) return lib;
lib = await import(NEURON_ESM_URL);
return lib;
};

const validateOrError = async (scriptValue, contextValue) => {
const { validateScript, validateExecutionContext } = await loadNeuron();
const scriptValidation = validateScript(scriptValue);
if (!scriptValidation.ok) {
return { error: { error: "invalid_script", validation_errors: scriptValidation.errors } };
}
const contextValidation = validateExecutionContext(contextValue);
if (!contextValidation.ok) {
return { error: { error: "invalid_context", validation_errors: contextValidation.errors } };
}
return {};
};

const registerNeuronTools = (mcp) => {
mcp.registerTool(
"validate_script",
"Validate a neuron-js ExecutionScript (pure JSON rules) without executing it. Returns ok plus validation errors.",
scriptSchema,
async (args) => {
const { validateScript } = await loadNeuron();
return text(validateScript(asRecord(args.script)));
},
);

mcp.registerTool(
"execute_decision",
"Validate and execute an ExecutionScript against an ExecutionContext. Returns the summarized output.",
scriptContextSchema,
async (args) => {
const scriptValue = asRecord(args.script);
const contextValue = asRecord(args.context);
const failure = await validateOrError(scriptValue, contextValue);
if (failure.error) return text(failure.error);

const { Neuron, Synapse, summarizeExecutionOutput } = await loadNeuron();
const result = new Synapse(new Neuron()).execute(scriptValue, contextValue);
return text(summarizeExecutionOutput(result));
},
);

mcp.registerTool(
"explain_decision",
"Validate, execute, and explain an ExecutionScript. Returns the summarized output plus the explanation trace.",
scriptContextSchema,
async (args) => {
const scriptValue = asRecord(args.script);
const contextValue = asRecord(args.context);
const failure = await validateOrError(scriptValue, contextValue);
if (failure.error) return text(failure.error);

const { Neuron, Synapse, summarizeExecutionOutput, explainExecution } = await loadNeuron();
const result = new Synapse(new Neuron()).execute(scriptValue, contextValue);
return text({
summary: summarizeExecutionOutput(result),
explanation: explainExecution({ script: scriptValue, result }),
});
},
);

mcp.registerPrompt(
"pricing-rules-example",
"Show the runnable pricing-rules example: a complete ExecutionScript JSON ready for validate_script or execute_decision.",
[],
() => ({
messages: [
{
role: "user",
content: {
type: "text",
text: "Load the pricing-rules example script and run validate_script on it.",
},
},
],
}),
);

mcp.registerResource(
"llms-txt",
"The compact AI-readable index of the neuron-js documentation.",
{ uri: "https://sebasoft.github.io/neuron-js/llms.txt", mimeType: "text/plain" },
async (uri) => {
const response = await fetch(uri);
return { contents: [{ uri, mimeType: "text/plain", text: await response.text() }] };
},
);

mcp.registerResource(
"skill-md",
"The official neuron-js AI skill (SKILL.md) served by this documentation site.",
{ uri: "https://sebasoft.github.io/neuron-js/skills/neuron-js/SKILL.md", mimeType: "text/markdown" },
async (uri) => {
const response = await fetch(uri);
return { contents: [{ uri, mimeType: "text/markdown", text: await response.text() }] };
},
);
};

const registerNativeTools = (modelContext) => {
modelContext.registerTool({
name: "validate_script",
description:
"Validate a neuron-js ExecutionScript (pure JSON rules) without executing it. Returns ok plus validation errors.",
inputSchema: {
type: "object",
properties: {
script: {
type: "object",
description:
"ExecutionScript JSON: { id, rules: [{ id, type: 'simple_rule', options, conditions: [...], actions: [...] }] }",
},
},
required: ["script"],
},
async execute({ script }) {
const { validateScript } = await loadNeuron();
return { content: [{ type: "text", text: JSON.stringify(validateScript(asRecord(script)), null, 2) }] };
},
});

modelContext.registerTool({
name: "execute_decision",
description:
"Validate and execute an ExecutionScript against an ExecutionContext. Returns the summarized output.",
inputSchema: scriptContextSchema,
async execute({ script, context }) {
const scriptValue = asRecord(script);
const contextValue = asRecord(context);
const failure = await validateOrError(scriptValue, contextValue);
if (failure.error) return { content: [{ type: "text", text: JSON.stringify(failure.error, null, 2) }] };

const { Neuron, Synapse, summarizeExecutionOutput } = await loadNeuron();
const result = new Synapse(new Neuron()).execute(scriptValue, contextValue);
return { content: [{ type: "text", text: JSON.stringify(summarizeExecutionOutput(result), null, 2) }] };
},
});

modelContext.registerTool({
name: "explain_decision",
description:
"Validate, execute, and explain an ExecutionScript. Returns the summarized output plus the explanation trace.",
inputSchema: scriptContextSchema,
async execute({ script, context }) {
const scriptValue = asRecord(script);
const contextValue = asRecord(context);
const failure = await validateOrError(scriptValue, contextValue);
if (failure.error) return { content: [{ type: "text", text: JSON.stringify(failure.error, null, 2) }] };

const { Neuron, Synapse, summarizeExecutionOutput, explainExecution } = await loadNeuron();
const result = new Synapse(new Neuron()).execute(scriptValue, contextValue);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
summary: summarizeExecutionOutput(result),
explanation: explainExecution({ script: scriptValue, result }),
},
null,
2,
),
},
],
};
},
});
};

const initWebMcp = () => {
// Prefer the native W3C WebMCP API when the origin trial enables it
// (navigator.modelContext); the WebMCP widget remains the universal path.
const nav = typeof navigator !== "undefined" ? navigator : undefined;
const modelContext = nav?.modelContext;

if (modelContext?.registerTool) {
try {
registerNativeTools(modelContext);
console.info("[neuron-js webmcp] native navigator.modelContext tools registered");
} catch (error) {
console.warn("[neuron-js webmcp] native registration failed, widget-only", error);
}
}

if (typeof WebMCP === "undefined") {
console.warn("[neuron-js webmcp] WebMCP global not found; widget disabled");
return;
}

const mcp = new WebMCP({
color: "#7c3aed",
position: "bottom-right",
size: "36px",
padding: "20px",
});

registerNeuronTools(mcp);
};

if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initWebMcp);
} else {
initWebMcp();
}
Loading
Loading