npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

roc-syntax-mcp

v0.0.3

Published

MCP server for Roc Lang: language, builtins, and platforms reference, and function signature search

Readme

Roc Syntax MCP Server

An MCP server that helps you write Roc, both applications on a platform and standalone scripts. The server teaches your coding agent the language and its idioms. It lets the agent check and format the code that it wrote, and it finds functions by type signature, as Hoogle does.

The server bundles the language reference, the signatures of all builtin methods, usage topics, and the basic-cli and basic-webserver platforms. Third-party plugins can add more platforms and packages.

Install

Requires Node 20.11 or newer, or Bun 1.1 or newer.

npm i -g roc-syntax-mcp

To upgrade the server and every plugin, run roc-syntax-mcp upgrade, then restart your AI harness. For pnpm, Yarn, Bun, or the latest unreleased commit, see Running with other package managers. To run it with no install, see Running without installing. For plugins, see Registering third party plugins. To remove the server, see Uninstalling.

Connect to your AI harness

The server speaks MCP over stdio.

| Client | Add the server | |---|---| | Claude Code | claude mcp add --scope user roc-syntax -- roc-syntax-mcp | | Codex CLI | codex mcp add roc-syntax -- roc-syntax-mcp | | Gemini CLI | gemini mcp add --scope user roc-syntax roc-syntax-mcp | | VS Code | code --add-mcp '{"name":"roc-syntax","command":"roc-syntax-mcp"}' | | Cursor | The JSON below, in ~/.cursor/mcp.json, or in .cursor/mcp.json for one project | | Windsurf | The JSON below, in ~/.codeium/windsurf/mcp_config.json | | Claude Desktop | The JSON below, in ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or ~/.config/Claude/claude_desktop_config.json (Linux) | | Zed | "context_servers": { "roc-syntax": { "command": "roc-syntax-mcp", "args": [] } } in its settings.json |

{
  "mcpServers": {
    "roc-syntax": {
      "command": "roc-syntax-mcp"
    }
  }
}

For Claude Code, --scope user makes the server available in every project. Use --scope project to store the entry in .mcp.json and commit it for your team.

To check the connection, ask the agent to call get_roc_syntax with no arguments. The reply should contain the language and builtin pages, not an error.

Running with other package managers

| Package manager | Minimum version | Command | |---|---|---| | npm | The one that comes with Node 20.11 | npm install -g roc-syntax-mcp | | pnpm | 7.0 | pnpm add -g roc-syntax-mcp | | Yarn 1 | 1.7 | yarn global add roc-syntax-mcp | | Yarn 2 and newer | 3.2.2 | None. Run yarn dlx -q roc-syntax-mcp in place of roc-syntax-mcp | | Bun | 1.1 | None. Run bunx roc-syntax-mcp in place of roc-syntax-mcp |

pnpm installs a global command only after you run pnpm setup one time. Yarn 2 and newer have no global install. Keep the -q in the yarn dlx command. Without it, Yarn writes its progress to stdout, and your client cannot read the server's replies.

When the command is more than one word, give your client the first word as the command and the rest as arguments. For Bun, in Claude Code:

claude mcp add --scope user roc-syntax -- bunx roc-syntax-mcp

To run the latest unreleased commit, install from the git repository. This install needs Node 22.15 or newer, because the server then runs its TypeScript source with no build step:

npm install -g github:Karakatiza666/roc-syntax-mcp

pnpm needs 8.7.6 or newer for this install. Older releases run the prepublishOnly script of a git package, and that script needs TypeScript, which is not installed. Yarn 1 installs it with yarn global add github:Karakatiza666/roc-syntax-mcp.

Running without installing

Your client can run the server with npx, with no install:

{
  "mcpServers": {
    "roc-syntax": {
      "command": "npx",
      "args": ["-y", "roc-syntax-mcp@latest"]
    }
  }
}

In Claude Code:

claude mcp add --scope user roc-syntax -- npx -y roc-syntax-mcp@latest

npx asks npm for the newest release at each start. You get updates with no upgrade command, but the server starts more slowly and needs the network. Run the other commands in this README the same way, for example npx -y roc-syntax-mcp@latest plugin add @roc-syntax/roc-ray.

Uninstalling

roc-syntax-mcp uninstall

The command lists what it removes, and asks you to confirm. --yes skips the question. Then the command does these steps:

  1. It deletes the plugins that plugin add installed.
  2. It deletes the nightlies that roc install downloaded, and the record of the compiler that the server runs. A compiler that you chose with roc use stays.
  3. It removes the server with the package manager that installed it.

A package manager cannot do steps 1 and 2, because npm, pnpm, Yarn and Bun run no script when they remove a package. Thus npm uninstall -g roc-syntax-mcp alone leaves the plugins and the compiler in the server's data folder. plugin doctor shows the path of that folder.

| Install | Step 3 | |---|---| | A global install by npm, pnpm, Yarn 1 or Bun | Removes the server | | npx, bunx, pnpm dlx or yarn dlx | Removes nothing, because the runner keeps its copy in its own cache. Run the command as npx -y roc-syntax-mcp@latest uninstall | | A git checkout | Removes nothing. Delete the folder of the checkout |

Your AI harness keeps its entry for the server. In Claude Code, run claude mcp remove roc-syntax.

Features

Tools

| Tool | What it does | |------|--------------| | get_roc_syntax | Call this tool first, with no arguments. It returns the whole language and its builtins as two compact pages, which answer most questions with no second call. The server also points to this tool in the MCP instructions that it sends when the client connects. With topic, it returns a topic example by name or keyword, a package page by the package name, or a worked program by its corpus and file name, such as basic-cli/hello. A question in words returns the one topic that covers clearly more of its words than any other. Without such a topic, it returns the closest topics, worked programs and symbols, each as the call that reads it. With scope and no topic, it returns the page of that corpus | | list_roc_index | Every index, selected by kind: scopes, topics, modules, examples | | search_symbols | Builtins and platform APIs by name, by type, or both, written as a Roc annotation. A name (concat, Str.concat) returns the signature and docstring, else the names that contain it, else the symbols whose module or docs contain the words. A type is a Hoogle-style structural search: List(x), (x -> y) -> List(y) finds List.map, and -> Bool searches by return type. ceil : -> Dec lists the names that contain ceil, ranked by type. One call takes up to 8 queries | | search_project_symbols | The search of search_symbols, by name, type, or both, over the .roc files in your own project. Each entry gives its file:line. With this tool, an agent can find the API of a platform that this server does not bundle | | get_roc_module | Every function and type of one module as a signature list: a builtin (Str, U64, ...), a module of the app's platform or a documented package. detail: "full" includes the original docstrings. For a module of your project, it names the file to read | | roc_check | Runs roc check over a code string or a path. It wraps code that has no header in a verified app for the platform in scope, and it reports line numbers in your own source | | roc_fmt | Runs roc fmt the same way, and returns the canonical formatting. A path is read, never written |

roc_check and roc_fmt need the Roc compiler, a 2026 nightly. See The Roc compiler. Every other tool reads bundled files and works offline. The only exception is the first call in a workspace, which can download a release that the app header pins. All tools are read-only, so a client that prompts for approval does not prompt for these tools.

The Roc compiler

The server looks for the compiler again on every call, in this order:

| Order | Source | |---|---| | 1 | --roc=<path> on the command line your client starts the server with | | 2 | The ROC environment variable | | 3 | The compiler that roc-syntax-mcp roc install or roc-syntax-mcp roc use recorded | | 4 | roc on the PATH the server started with |

| Command | Does | |---|---| | roc-syntax-mcp roc | Shows which compiler the server runs, where that choice came from, and the nightly the server was checked against | | roc-syntax-mcp roc install [nightly] | Downloads a nightly from roc-lang/nightlies into the server's own folder, checks its sha256 against the release, and records it. Defaults to the nightly the server was checked against. About 70 MB | | roc-syntax-mcp roc use <path> | Records a compiler you already have |

A running server uses the compiler that these commands record from its next call, with no restart. Thus an agent can install the compiler in the same session. When no compiler runs, roc_check replies with the exact command to run.

Topics

Topics are worked examples. Each topic shows one construct or one task, not a whole program. The language topics also carry every rule of upstream's language reference, as comments, expects and # @rejects / # @warns blocks that check:roc compiles. list_roc_index(kind: "topics") prints them with descriptions. The agent gets a topic through get_roc_syntax(topic:), by its name or by a question.

| Group | Topics | |---|---| | Language | idioms, iterators, ranges, record_fields, derived_methods, json, hashing, operators, pattern_matching, list_patterns, tag_unions, try_operator, strings, effects, loops, conditionals, tuples, records, types, naming, numbers, opaque, nominal, functions, compile_time, memory, dict_set, static_dispatch, imports, modules, packages, comments, testing, compiler, app_header, dbg_crash, platforms, platform_abi, builder_pattern, scripting, error_design | | basic-webserver | webserver_handler, webserver_sqlite, webserver_sse, webserver_html, webserver_forms, webserver_static | | basic-cli | cli_app, cli_files, cli_command, cli_http, cli_sqlite, cli_terminal, cli_net | | roc-parser | parser_combinators, parser_csv, parser_yaml, parser_json, parser_xml, parser_markdown, parser_asciidoc, parser_roc_syntax, parser_http, parser_compile_time | | roc-random | random_generators |

The platform and package topics are complete apps. Each one passes roc check against the pinned release.

Resources

Clients that support MCP resources can attach these without a tool call:

| URI | What it is | |-----|------------| | roc-syntax://overview | Both overview pages in one read. Attach this resource first | | roc-syntax://reference | The full all_roc_syntax.roc reference | | roc-syntax://builtin | Index of the builtin modules and their method counts | | roc-syntax://topic/{name} | One topic, with tab-completion of {name} |

Choosing the platform

A Roc app pins exactly one platform, so the server uses the data of at most one platform at a time.

| Scope | What it holds | |-------|---------------| | language | The language itself: syntax, types, modules, operators, and the upstream language reference. Always available | | builtin | The standard library. Every builtin module, in scope with no import. Always available | | basic-webserver | Platform: HTTP server, SQLite, files, commands, SSE, plus the roc-lang/http types it re-exports | | basic-cli | Platform: stdio, files and paths, commands, HTTP client, SQLite, TCP, TTY, env, locale, random, time, plus the same roc-lang/http types |

In an existing project, the server finds the platform in the app [...] header of your .roc files. A new project has no header yet, so the server lists the platforms that it can serve, and the AI agent helps you select one. To skip this question, name the platform on the command line that your client starts the server with:

roc-syntax-mcp --platform=basic-cli

To do this for one project only, put the flag in the project's own client config, such as the .mcp.json that claude mcp add --scope project writes.

The AI agent can pass an explicit scope argument on a tool call to look up data for a specific platform.

Automatic indexing of packages

The server documents the releases in the table below. list_roc_index(kind: "scopes") gives the release of each platform and package. The UPSTREAM file beside each corpus records its source, and for the language, the commit.

| Content | Kind | Contents | |---|---|---| | The language and builtins | Language | Syntax, the language reference, every builtin module | | basic-webserver | Platform | HTTP server, SQLite, files, commands, SSE | | basic-cli | Platform | stdio, files and paths, commands, HTTP client, SQLite, TCP, TTY | | http | Package | The HTTP types both platforms re-export | | roc-parser | Package | Parser combinators, and parsers for CSV, YAML, JSON, XML, Markdown, AsciiDoc, Roc source and HTTP/1.1 messages that decode into your own types or check a literal during compilation | | roc-random | Package | Pseudorandom values from a seed |

The packages work on any platform. get_roc_syntax(topic: "roc-parser") returns a package's overview page.

Signature search covers every package that your app header pins, including packages that this server does not document. You do not configure anything. The server reads your app header and indexes each release that it pins. A search finds the functions of a pinned package together with the builtins.

| Your header pins | Signatures come from | |---|---| | A release in the table above | The index that ships with the server | | Any other release, in the compiler's package cache | That release, read from ~/.cache/roc/packages/<hash>/ | | Any other release, not in the cache, a compiler the server can run | That release. The server runs roc deps one time to download it, and roc deps checks it against the hash in its URL. The first answer waits up to 60 seconds for the download. Until the download is done, later answers come from the documented release, with a note | | Any other release, not in the cache, no roc | For a platform or package in the table, its documented release, with a note that the two differ. For any other package, nothing, with a note that says so |

The work is cached at two levels:

| Cache | Holds | Kept until | |---|---|---| | The compiler's package cache | The downloaded release | You delete it. Thus roc deps runs only one time for each release | | The server's memory | The parsed index | The server stops, or your app header changes |

If the header does not pin a package from the table, a lookup does not return the functions of that package as results. The answer tells which package has the function, shows up to three matching signatures, and gives the line to add to your app header.

The server serves the overview, topics and examples for any release that you pin, even across a major version. They were written for the documented release, and most of what they teach stays true. Only the signatures follow your pin.

Before a project has an app header, name the platform in scope. The overview of that platform shows an app header to start from. After you write the header, the server finds it within seconds, so later calls need no scope.

The server makes no network connection itself. Set ROC_PACKAGE_CACHE when your compiler keeps packages somewhere else.

Registering third party plugins

Add a plugin, then restart your AI harness:

roc-syntax-mcp plugin add @roc-syntax/roc-ray

| Command | Does | |---|---| | plugin add <package>... | Installs from npm, or from a path or tarball, and serves the plugin in every workspace. Refuses and undoes the install of a package that the server would not load, or of one that takes a name from an installed plugin. If add refuses a new release, the old release stays installed | | plugin update [name...] | Installs the newest release of every plugin that add installed, or of the named ones. Each plugin updates separately, so a refused release does not stop the update of other plugins. A plugin linked to a folder always serves the contents of the folder | | plugin remove <name>... | Uninstalls a plugin that add installed. Takes the package name, or the path you added it from | | plugin list | Shows what add installed, its version, the release each corpus documents, and the folder it is in. Fails when an installed package is missing, for example a path plugin whose folder you deleted | | plugin doctor | Shows the set a server started here would load, and why it skipped anything. It also shows the data folder that holds the plugins and the compiler |

A plugin is an npm package that holds only text: Roc source, docs and JSON. No code in a plugin runs, and add runs no install scripts. add installs the plugin into the server's own folder, which plugin list shows. The server then serves the plugin in every workspace.

add also takes a git source that npm accepts:

roc-syntax-mcp plugin add github:<owner>/<repo>#<tag>

The repository root must hold plugin.json. plugin update does not update a git install. To update it, plugin add the new tag.

roc-syntax-mcp upgrade updates the server, then runs plugin update in the new server, because the new server decides which plugins it serves. The command updates a global install made by npm, pnpm, Yarn 1 or Bun. It updates an install from the git repository from the repository again. For npx, bunx, pnpm dlx, yarn dlx or a git checkout, it tells you what to run, and it updates no plugin. The server never updates itself or a plugin when it starts.

add runs npm, or Bun when the server runs under Bun, so your .npmrc registry and login settings apply. add works for every type of server install, including npx, bunx and yarn dlx.

You can also name a plugin in two other places:

  • On the command line that your client starts the server with, as roc-syntax-mcp --plugin=@roc-syntax/roc-ray.
  • In ROC_MCP_PLUGINS, as a comma-separated or colon-separated list.

The value is a path to a plugin folder, or the name of a package. The package must be one that plugin add installed, or one that your package manager installed globally beside the server. npm, Yarn 1, Bun, and pnpm up to 10 can do this. pnpm 11 and newer keep each global package apart from the others, so use plugin add with them. To serve a plugin in one project only, put the flag in the project's own client config, such as .mcp.json. The server reads all of these values at startup, so restart it after a change.

If the server cannot find or load a plugin, it skips that plugin and serves the rest. The server writes the reason to its log, and the first get_roc_syntax answer gives the reason to the agent.

Plugins

A plugin can document a platform or package that this server does not bundle. The same tools answer for a plugin, in the same shape as for a bundled platform.

This repository maintains the plugins below. Each one shows a different type of plugin:

| Plugin | Kind | What it shows | |---|---|---| | @roc-syntax/roc-ray | Platform | RocRay, for games, graphics and sound. A platform plugin brings its own app scaffold, so roc_check wraps a bare snippet in a RocRay app. The plugin also pins its own compiler nightly, older than the server's, because the release was built and tested with that nightly | | @roc-syntax/weaver | Package | Weaver, a command-line argument parser. A package plugin has no scaffold. In any app that pins the package, on any platform, its functions are searched with the builtins. The plugin is UPL-1.0, and its LICENSE retains the MIT notice of the project that weaver forks | | @roc-syntax/cloudflare-workers | Platform | cloudflare-workers, for Cloudflare Workers in Roc: HTTP, D1, KV, R2, Queues, Durable Objects and RPC. The release is a bundle with a host and a JS shim. The plugin gives the project files (talon.toml, worker.js, wrangler.toml) that the platform needs, and it pins its own compiler nightly | | @roc-syntax/joy | Platform and package | Joy, for browser apps in WebAssembly, and joy-html, the package that Joy views are written in. One plugin holds both. The server refuses the plugin unless its joy-html is the release that the Joy platform pins. render returns joy-html's Html type, and the Html of another release is a different type |

To write your own plugin, read plugins/README.md and docs/design/plugins.md. roc-syntax-mcp plugin init creates the skeleton of a new plugin.

Commercial use

This project aims to help grow the Roc community. You are welcome to use this server in a hobby, educational or professional setting.

Commercial use of this server is permitted. Using, modifying and hosting it carry no obligations. Obligations apply only when you distribute copies to others in any form (source code, a build, or an image). They then apply only to the files of this server that you distribute.

License says which license applies to which files.

| Use | Obligation | |---|---| | Closed-source, commercial or client work | None | | Shipping what an agent wrote with its help | None. The output is yours | | Team-wide installs, CI | None | | Modifying it for internal use | None. Internal use is not distribution | | Hosting it, modified or not, for your engineers or for anyone | None. Hosting is not distribution | | Paid consulting, training or workshops that use it, selling support or services around it | None | | Redistributing it unmodified, in an image or an internal mirror | Keep the copyright notices, and include LICENSE and LICENSES/ | | Distributing a modified copy, as a fork, a package or an image | The same, and make the source of the server's files you changed available under MPL-2.0. The public repository of a fork is enough | | Shipping the server inside a larger product with closed-source parts | The same. The closed-source parts stay closed | | Publishing a plugin under any license, including for your own commercial platform | None. A plugin is a separate package, not a derivative. What it vendors carries its own license |

Not permitted: distributing the server's own files, modified or not, without their source, or under terms that remove MPL-2.0 from them.

License

SPDX-License-Identifier: MPL-2.0 AND UPL-1.0

The server is licensed under MPL-2.0, so that changes to its files stay open source, and any company can use it. MPL applies file by file. When you distribute a copy, the server's own files, with your changes to them, stay under MPL-2.0. The files that you add beside them can have any license, including a closed-source license.

The bundled Roc corpus is licensed under UPL-1.0, the license of the platforms and packages that it documents. Examples and snippets that an agent takes from the corpus carry no obligations into your codebase.

| File | Holds | |---|---| | LICENSE | The map of licenses per file path | | REUSE.toml | The same map, in a machine-readable format | | THIRD-PARTY-NOTICES.md | The upstream copyright lines that UPL requires with the copies |

Troubleshooting

| Symptom | Cause | |---|---| | The client lists no tools | The entry is not connected. claude mcp list, or check the client's MCP log | | roc-syntax-mcp: command not found in a client that works in your shell | The client did not inherit PATH. Use the absolute path from which roc-syntax-mcp | | env: 'node': No such file or directory | The machine has Bun and no Node. Start it as bunx roc-syntax-mcp | | roc_check reports the compiler is missing | The server found no compiler. Run roc-syntax-mcp roc install, or roc-syntax-mcp roc use <path>. No restart is needed. Every other tool still works | | Answers name a platform you are not using | The server found no app header, or the wrong one. Set scope explicitly, see Choosing the platform | | "No platform detected" in a project with an app header | The client sends no project folder, and the server started outside the project. Add --workspace=, see How the server finds your project | | A plugin you installed is not served | Your package manager installed it and no flag names it, it is a pnpm 11 global install, or the server started before the change. Use roc-syntax-mcp plugin add, or run roc-syntax-mcp plugin doctor | | A warning that your platform version differs from the bundled one | Expected when the server cannot read the release. After roc fetches the release that your header pins, the signatures come from that release. The overview and topics stay those of the bundled release. The note appears once per session | | The first answer in a new workspace takes up to a minute | Your app header pins a release this server does not bundle, and roc deps is downloading it. Later answers read it from the compiler's cache |

How the server finds your project

The server reads the app [...] header in your project to know the platform. To find the app header, the server needs the project folder. Usually the client sends it. The server uses the first source in this table that gives a folder:

| Order | Source | Set by | |---|---|---| | 1 | --workspace=<dir> | You, in the client config. It overrides the folder that the client sends. Cursor and VS Code replace ${workspaceFolder} with the open folder | | 2 | The MCP roots that the client sends | Clients that support roots, such as Cursor | | 3 | The CLAUDE_PROJECT_DIR environment variable | Claude Code | | 4 | The working directory of the server process | The client, when it starts the server. This is a fallback, used only when no source above gives a folder |

Source 4 is correct only when the client starts the server in the project folder. Many clients start the server in their own working directory. That directory is often the project folder, but not always.

If list_roc_index with kind: "scopes" says "No platform detected" in a project that has an app header, the client sends no project folder, and the server started outside the project. Set the folder with --workspace=. Give your project's path, or ${workspaceFolder} if the client supports it:

{
  "mcpServers": {
    "roc-syntax": {
      "command": "roc-syntax-mcp",
      "args": ["--workspace=${workspaceFolder}"]
    }
  }
}

If the --workspace= value is not a folder, the server ignores it and writes a line to the client's MCP log.

Contributing

Pull requests of all sizes are welcome. Give the reason for each pull request. Do not divide a large, cohesive pull request only to keep each part small. CONTRIBUTING.md covers the repository layout, the check scripts, how to refresh the bundled content from upstream, and how to write a plugin.

You license your contribution under UPL-1.0. You keep your copyright, everyone may use the contribution, and the project may ship it under other terms, such as another open-source license. Each commit carries a one-line agreement to CLA.md. See the section "Contribution license" in CONTRIBUTING.md for how to add the line.