@panagos/jojoscript 0.9.1 → 0.9.3

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 CHANGED
@@ -1,4 +1,4 @@
1
- # JojoScript 0.9.1
1
+ # JojoScript 0.9.3
2
2
 
3
3
  JojoScript is a small JavaScript-compatible language and runtime for
4
4
  expressive, composable pipelines. It adds lightweight syntax for declarations,
@@ -6,8 +6,8 @@ functions, pattern matching, type annotations, and pipelines while preserving
6
6
  ordinary JavaScript. Its compiler uses a lightweight lexer and source
7
7
  transformations rather than a full AST, keeping the toolchain compact. The
8
8
  project also provides lazy and async collection operators, checkpointing,
9
- retries, metrics, pipeline graphing, CLI workflows, effect handlers, and
10
- VS Code editor support.
9
+ retries, metrics, pipeline graphing, CLI workflows, effect handlers, MCP
10
+ support, and VS Code editor support.
11
11
 
12
12
  See [CHANGELOG.md](CHANGELOG.md) for release history and
13
13
  [example/](example/) for a complete runnable application that exercises
@@ -739,6 +739,29 @@ never through `|>`) and also uses pipeline syntax with overlapping names,
739
739
  the compiler merges the auto-imported names into the existing import
740
740
  instead of emitting a second, colliding import declaration.
741
741
 
742
+ ### MCP support
743
+
744
+ JojoScript includes a small MCP-style JSON-RPC runtime for Node.js. It does
745
+ not add MCP-specific language syntax; tool implementations remain ordinary
746
+ JavaScript functions.
747
+
748
+ ```js
749
+ import { createMcpServer, runMcpStdioServer } from "jojoscript/runtime"
750
+
751
+ const server = createMcpServer({
752
+ tools: {
753
+ echo: args => ({ content: [{ type: "text", text: args.value }] })
754
+ }
755
+ })
756
+
757
+ await runMcpStdioServer(server)
758
+ ```
759
+
760
+ The server accepts `tools/call` requests and returns JSON-RPC responses on
761
+ standard output. It is intentionally minimal: supporting tool registration,
762
+ request dispatch, structured errors, and the stdin/stdout transport without any
763
+ external NPM packages.
764
+
742
765
  ## Example
743
766
 
744
767
  See [example/](example/). It is a complete runnable project covering every
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panagos/jojoscript",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "A tiny JavaScript-compatible language layer with Jojo syntax and pipelines.",
5
5
  "license": "Unlicense",
6
6
  "type": "module",
@@ -327,6 +327,14 @@ export function emitPipeline(pipeline, options = {}) {
327
327
  continue
328
328
  }
329
329
 
330
+ if (stage.name === 'pass') {
331
+ if (stage.args.length === 0) continue
332
+ if (stage.args.length === 1 && stage.args[0] === '_') {
333
+ continue
334
+ }
335
+ throw new SyntaxError('the `pass` pipeline stage takes no arguments or a single `_` placeholder')
336
+ }
337
+
330
338
  if (stage.name.startsWith('.')) {
331
339
  const args = stage.args.slice()
332
340
  const placeholderIndex = args.indexOf('_')
@@ -67,4 +67,9 @@ export {
67
67
  MemoryEffectLog,
68
68
  FileEffectLog
69
69
  } from './effect-log.js'
70
+ export {
71
+ McpServer,
72
+ createMcpServer,
73
+ runMcpStdioServer
74
+ } from './mcp.js'
70
75
 
@@ -0,0 +1,93 @@
1
+ /*
2
+ * Minimal MCP-style JSON-RPC transport for Node.js. The server is protocol-
3
+ * focused rather than introducing MCP-specific JojoScript syntax; tool
4
+ * implementations remain ordinary JavaScript functions.
5
+ */
6
+
7
+ import { createInterface } from 'node:readline'
8
+
9
+ export class McpServer {
10
+ constructor({ tools = {} } = {}) {
11
+ this.tools = new Map(Object.entries(tools))
12
+ }
13
+
14
+ async handleRequest(message) {
15
+ if (!message || typeof message !== 'object') {
16
+ return {
17
+ jsonrpc: '2.0',
18
+ id: null,
19
+ error: {
20
+ code: -32600,
21
+ message: 'Invalid Request'
22
+ }
23
+ }
24
+ }
25
+
26
+ if (message.method !== 'tools/call') {
27
+ return {
28
+ jsonrpc: '2.0',
29
+ id: message.id ?? null,
30
+ error: {
31
+ code: -32601,
32
+ message: 'Method not found'
33
+ }
34
+ }
35
+ }
36
+
37
+ const toolName = message.params?.name
38
+ const toolArgs = message.params?.arguments ?? {}
39
+ const tool = this.tools.get(toolName)
40
+
41
+ if (!tool || typeof tool !== 'function') {
42
+ return {
43
+ jsonrpc: '2.0',
44
+ id: message.id ?? null,
45
+ error: {
46
+ code: -32602,
47
+ message: `Unknown tool: ${toolName}`
48
+ }
49
+ }
50
+ }
51
+
52
+ try {
53
+ const result = await tool(toolArgs)
54
+ return {
55
+ jsonrpc: '2.0',
56
+ id: message.id ?? null,
57
+ result
58
+ }
59
+ } catch (error) {
60
+ return {
61
+ jsonrpc: '2.0',
62
+ id: message.id ?? null,
63
+ error: {
64
+ code: -32603,
65
+ message: error instanceof Error ? error.message : String(error)
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ export function createMcpServer(options = {}) {
73
+ return new McpServer(options)
74
+ }
75
+
76
+ export async function runMcpStdioServer(server, options = {}) {
77
+ const input = options.input ?? process.stdin
78
+ const output = options.output ?? process.stdout
79
+
80
+ const lines = Array.isArray(input)
81
+ ? input.values()
82
+ : createInterface({ input, crlfDelay: Infinity })
83
+
84
+ for await (const line of lines) {
85
+ if (!line.trim()) continue
86
+
87
+ const message = JSON.parse(line)
88
+ const response = await server.handleRequest(message)
89
+ output.write(`${JSON.stringify(response)}\n`)
90
+ }
91
+
92
+ return 0
93
+ }