{"name":"mcp_echo_server.nu","source":"// mcp_echo_server.nu — a complete MCP server in NURL, over stdio.\n//\n// Register the tools, pick a transport, done. Everything below the\n// registration — JSON-RPC framing, the dual-era version gate,\n// `server/discover`, `_meta` decorations, per-handler panic isolation —\n// is `stdlib/ext/mcp_server.nu`'s job, and you should never write it\n// again. (Three servers in this repo did, from an earlier version of\n// this very example, and their copies drifted apart.)\n//\n// Run by hand:\n//\n//     ./build.sh\n//     ./nurl.sh examples/mcp_echo_server.nu\n//     printf '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{}}\\n'\\\n//            '{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}\\n'\\\n//            '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\"}\\n'\\\n//            '{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",'\\\n//                '\"params\":{\"name\":\"echo\",\"arguments\":{\"text\":\"hello\"}}}\\n' \\\n//       | ./examples/mcp_echo_server\n//\n// Or wire it into Claude Desktop / claude.ai by adding an MCP server\n// entry whose `command` invokes the compiled binary.\n//\n// Serving the same tools over HTTP instead is one line — see\n// examples/mcp_echo_server_http.nu.\n\n$ `stdlib/ext/mcp.nu`\n$ `stdlib/ext/mcp_server.nu`\n$ `stdlib/core/string.nu`\n\n// ── The tool ────────────────────────────────────────────────────────\n//\n// A handler takes the call's `arguments` object and returns a\n// tool-result envelope. It may fail however it likes: a panic in here\n// becomes an error envelope for this one call, not a dead server.\n\n@ echo_tool Json args → Json {\n    : ?Json text_j ( json_obj_get args `text` )\n    ?? text_j {\n        T tv → { ^ ( mcp_tool_result_text ( json_str_data tv ) ) }\n        F _ → { ^ ( mcp_tool_result_error `missing required argument: text` ) }\n    }\n}\n\n@ main → i {\n    : McpServer srv ( mcp_server_new `nurl-echo` `1.0.0` )\n\n    // Instructions ride `server/discover` and the initialize result:\n    // how a model should approach this server. Optional, and the first\n    // thing a real server should write.\n    ( mcp_server_set_instructions srv\n    `One tool, echo, which returns the text you give it. Useful as a\nconnectivity check before trying anything that costs something.` )\n\n    // `_full` carries the ToolAnnotations. They matter: an ABSENT\n    // destructiveHint defaults to TRUE in the spec, so a tool listed\n    // without them is shown to the user as if it could destroy state,\n    // and clients that auto-allow read-only tools will not.\n    //             read-only  destructive  idempotent  open-world\n    ( mcp_server_add_tool_full srv `echo`\n    `Echo the supplied text back to the caller.`\n    ( mcp_schema_of1 `text` `string` `Text to echo` T )\n    T F T F\n    \\ Json a → Json { ^ ( echo_tool a ) } )\n\n    // Blocks until stdin closes. Handlers must outlive this call —\n    // the wrapper above captures nothing, so it lives as long as the\n    // program.\n    ?? ( mcp_server_serve_stdio srv ) {\n        T _ → {}\n        F e → { ( mcp_log ( mcp_server_err_name e ) ) }\n    }\n    ^ 0\n}\n","bytes":3182}