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

@bicon/mcp-composer

v0.3.5

Published

MCP server that helps an AI client compose a valid BICon portfolio initiative and hand it back as an import file (draft-first, no auth, no API calls).

Readme

BICon Initiative Composer (MCP server)

An MCP server that helps an AI client — e.g. Claude Desktop — set up a portfolio initiative conversationally. Creating an initiative by hand means filling ~15 metadata fields and rating 30 sub-dimensions across four scoring dimensions (plus optional economics and a large EU-AI-Act block); this server lets an assistant run that interview and produce a complete, valid initiative.

Draft-first by design — no auth, no API calls

The server holds no credentials and never talks to the BICon API. Its tools assemble and validate an initiative and hand back an import file; you load that file through the app's existing, authenticated import UI. The "Add as new → Confirm" step in the app is the human-in-the-loop — nothing reaches your organisation until you confirm it in the browser.

Why: the API only accepts short-lived browser session tokens (there is no API key), and a draft-first flow adds zero new attack surface. Live create/update via API keys is a separate, security-reviewed phase.

Tools & resource

| Name | What it does | |---|---| | resource initiative://schema / tool get_initiative_schema | The field catalog: required fields, enum values, the four dimensions with their named sub-dimensions (EN/DE labels + help text, plus the wording the app uses in digital / innovation portfolios), the exact shape of every optional section, the EU-AI-Act vocabularies, and the handoff steps. Read this first. | | assemble_initiative | Compact spec (metadata + named sub-ratings 0–100 + confidence per dimension + optional sections, optionally the organisation's context) → fully-formed initiative validated against the API contract (CreateInitiativeBody from @workspace/api-zod). Mints the initiative id and every nested row id (decision-log entries, status reports, incidents — a client never invents ids), derives the four dimension scores (the app's own scoring function), accepts dates as dd/mm/yyyy, dd.mm.yyyy or yyyy-mm-dd, returns {ok, initiative, derivedScores, warnings, errors, contextId}. Pass id (an existing initiative's) only to update that initiative. | | export_initiative_file | Takes the same compact spec, assembles it and wraps the result in the app's single-initiative JSON envelope; returns {ok, filename, json, importInstructions, derivedScores, warnings}. There is one pipeline — a hand-edited initiative object is not an input, so client-supplied scores or unknown keys can never bypass assembly. | | read_portfolio_context | Takes the organisation's portfolio-context file (see below) and returns the interview plan for that organisation — its own labels, help texts and rating notes per sub-dimension, which sub-dimensions are switched off, its business units, categories, tags, skills (id + name) and non-archived initiatives (id + name), currency, discount rate, whether the EU-AI-Act module is on — plus a short contextId that stands in for the file in the two other tools. | | composer_version | Returns {composerVersion, capabilities}. Every successful tool result also carries composerVersion. |

The portfolio context — how the Composer learns an organisation's settings

The Composer holds no credentials and calls no API, so on its own it cannot know how an organisation configured its portfolio. The app therefore exports a small portfolio-context JSON (Import/Export → the AI-assistant card → Download portfolio context): the active portfolio mode, which evaluation sliders are switched off, the organisation's own slider wording and rating notes, business units, categories, tags, the skill roster and the live initiatives (id + name), currency, discount rate and the AI-Act module switch. The user hands that file to the assistant at the start; the assistant calls read_portfolio_context once and then passes the returned contextId (ctx_<16 hex>, a content hash — or the whole file again) as context to assemble_initiative / export_initiative_file, so a 5–40 KB document is not re-emitted three times per initiative. The id resolves against a per-process content cache; an unknown id (server restarted) is a clear error naming the fix, never a wrong answer. Effects:

  • the interview uses the organisation's wording (org label › mode overlay › base) and skips switched-off sub-dimensions;
  • switched-off sub-dimensions are optional in the spec, stored as 0 (the app's blank default — a value sent anyway is zeroed with a warning) and excluded from the dimension score — the app's own derivation, so the score does not change on the first save in Evaluate; a dimension with every sub-dimension off needs only { confidence };
  • businessUnit and aiCategory must be on the organisation's lists (no inventing — matched case-insensitively and normalised to the organisation's spelling; new ones are created in the app's Settings); dependsOn, requiredSkills[].skillId and an update id must be ids that exist (any id format; Retired initiatives count, archived ones do not);
  • ratings must be on the organisation's slider step (Settings › Slider granularity: 5, 10, 20 or 25) — the plan states it, a value off the grid is rejected with the step in the message (files exported before 2026-08-21 carry no step and are not checked);
  • every EU-AI-Act field the app strips is warned about while the organisation's AI-Act module is off; the export file defaults to the context's mode and language; a file newer than the Composer knows is flagged.

Without the file everything falls back to the built-in catalog: business unit and category are free text, dependency / update ids must at least be UUID-shaped (zod's rule, the same the API uses), and scores are derived over all 30 sub-dimensions. Export the file again after changing settings — the plan reports its export date.

How validation works (and why it lives at the wire)

The MCP SDK validates every call with the registered schema and hands the handler the parsed value — whatever the schema strips, the server never sees. So the spec schema is registered as a full strict object, with every optional section a deep-strict copy of the API contract: an unknown key anywhere is rejected by the SDK with its exact path (Unrecognized key(s) in object: 'literacy' at aiAct), enum values, dates (dd/mm/yyyy, dd.mm.yyyy or yyyy-mm-dd — real calendar dates, normalised to yyyy-mm-dd) and blank names are caught with field paths, and the advertised JSON Schema (additionalProperties: false, no $ref pointers) means what it says. A section may also be sent as a JSON string (clients working from a stale tool descriptor did that); it is parsed and validated identically. What a JSON Schema cannot express — the 30 named ratings, the AI-Act controlled vocabularies (aiActVocabularies in the descriptor; the app drops unknown obligation keys and never shows unknown artifact keys), the score derivation — is checked in the assembler, which reports all problems of a call at once with the allowed values in the message (id shapes included). Row ids of decision-log entries, status reports and incidents are minted by the Composer — a client-supplied UUID is kept, anything else replaced — because the app keys those rows per initiative and a re-used id from another initiative is exactly what collided on import once.

Errors the SDK raises before a handler runs (Input validation error: …) carry no composerVersion — that is normal. A successful result without it means an old build is answering (see next section).

Stale instances (reinstall is not enough — quit the app)

Claude Desktop runs an extension in two places: a chat-UI instance, which every reinstall restarts, and a session-facing instance (LocalMcpServerManager in Desktop's main.log) that serves Claude Code, Cowork and remote claude.ai chats reaching the device. The session-facing instance is started once at app launch and is only replaced on a full app quit — reinstalling or disabling/enabling the extension leaves it running the old code. Observed on 2026-08-18: five reinstalls, the chat-UI copy was current every time, and every session still got the original build.

Tell: a successful tool result without composerVersion, or composer_version missing from the tool list. Fix: install the new .mcpb, then quit Claude Desktop completely (tray icon → Quit — closing the window is not enough), reopen, and check with composer_version.

One source for the domain

The Composer imports the app's own domain leaf, @workspace/initiative-model (types and value lists incl. the AI-Act vocabularies, the score derivation, the portfolio-mode wording overlays, the backup-format version) — the same package the web app renders and validates with — and derives its enum lists from the generated API contract. The 30 sub-dimension keys are the shared leaf's too; the only hand-listed part is their base wording, and catalog-drift.test.ts pins the keys to the contract in both directions. server.test.ts drives the real server through the real SDK client over an in-memory transport, so what is asserted is what a client sees.

Install for users

Claude Desktop — the extension (recommended, no Node needed)

Download bicon-composer.mcpb from the BICon app (Import/Export → Set up initiatives with an AI assistant, or directly at https://www.bicon.digital/downloads/bicon-composer.mcpb) and open it — double-click, or Claude Desktop → Settings → Extensions → Install. Claude Desktop runs the server on its own built-in Node runtime, so there is nothing else to install and no configuration file to edit. Then quit Claude Desktop completely and reopen (see Stale instances), start a new chat and ask: "Use the BICon Initiative Composer to help me set up a new initiative."

Why the extension is primary: current Claude Desktop builds manage local MCP servers as Desktop Extensions and treat claude_desktop_config.json as their own preferences store — a hand-pasted mcpServers block there is silently overwritten. There is no supported config-file route for Claude Desktop.

Other MCP clients — the npm package

@bicon/mcp-composer on npm is a single self-contained bundle whose only runtime dependency is the MCP SDK. Any MCP-capable client with Node ≥ 20 can run it; for a client that reads a mcpServers config, add this and restart the client:

{
  "mcpServers": {
    "bicon-composer": {
      "command": "npx",
      "args": ["-y", "@bicon/mcp-composer@latest"]
    }
  }
}

@latest matters: an unpinned npx spec keeps serving whatever it cached first. Check what you got with the composer_version tool — the registry version and the extension version are released together (see Publishing); if npm is behind, use the extension.

Prerequisite: Node.js ≥ 20 on the same operating system as the client. On Windows with Node only inside WSL, launch the server through WSL: "command": "wsl.exe", "args": ["-d", "Ubuntu", "--", "/path/to/npx", "-y", "@bicon/mcp-composer@latest"] (replace /path/to/npx with the output of which npx in WSL). Restart the client afterwards.

Build & run from source (contributors)

pnpm --filter @bicon/mcp-composer run build        # → dist/index.mjs (npm bundle) + dist/mcpb/composer-<v>.mjs (extension bundle)
pnpm --filter @bicon/mcp-composer run test         # vitest, incl. the SDK-boundary suite (builds the bundles itself)
pnpm --filter @bicon/mcp-composer run build:mcpb   # → dist/bicon-composer.mcpb, copied to the web app's public/downloads
node lib/integrations/mcp/dist/index.mjs           # stdio server (used by a client, not by hand)

For a client that reads a mcpServers config, point it at the local build with an absolute path ("command": "node", "args": ["/ABSOLUTE/PATH/TO/lib/integrations/mcp/dist/index.mjs"]; on Windows with the repo in WSL, run through wsl.exe with the WSL node). For Claude Desktop, build and install the extension instead.

build:mcpb stages the manifest (version, entry point and launch args written from package.json — the checked-in mcpb/manifest.json carries placeholders), adds the single self-contained bundle, and — before zipping — boots the staged server and drives it through the protocol: initialize (version), tools/list (five tools, no $ref), composer_version, resources/list, and an assemble_initiative call with an unknown AI-Act key that must be rejected by name. It then copies the .mcpb to artifacts/bicon-portfolio/public/downloads/ and writes src/lib/composer-extension.json (version + path), which the Import/Export card imports. Requires python3 for the zip container.

Using it (the interview → import flow)

  1. (Recommended) In the app, Import/Export → Download portfolio context, and give that file to Claude first — it then interviews with your organisation's own wording, skips switched-off sliders and only accepts your business units, categories, skills and initiatives.
  2. Ask Claude: "Help me set up a new initiative in BICon." It reads initiative://schema (and your context) and interviews you — metadata first, then the sub-dimensions with the app's help text, then a confidence per dimension. Dates are dd/mm/yyyy in conversation.
  3. Claude calls assemble_initiative and shows you the derived scores and any warnings as a sanity check (fix and re-run if anything looks off).
  4. Claude calls export_initiative_file with the same spec → save the returned JSON under the suggested filename (initiative-<name>-<date>.json). To update an existing initiative, name it — Claude passes its id from the context and the app offers "Update"; a re-export without the id is recognised by name and offered as an update too.
  5. In the app (signed in to the target organisation): if the initiative carries EU-AI-Act data, make sure the organisation's EU-AI-Act module is switched on (Settings › AI Act — the app strips AI-Act fields on import while it is off; the file's sourceMode is informational and cannot override that). Then Import/Export → Single initiative card → drop the file → the preview should show 0 warningsAdd as new (or Update, which keeps the existing initiative's id, creation date and links) → Confirm.
  6. The initiative appears in the Inventory with exactly the scores Claude reported.

Publishing (maintainers)

Release = extension + npm together, from one version:

cd lib/integrations/mcp
npm version patch|minor      # bumps package.json — the ONLY place the version is written
pnpm run test                # incl. the SDK-boundary suite
pnpm run build:mcpb          # bundles, ship-gate smoke, copies the .mcpb + version JSON into the web app
npm login                    # once, as the BICon npm account (business identity, 2FA on)
npm publish                  # prepack builds; publishConfig.access is public

Then commit (the .mcpb and composer-extension.json are part of the release commit) and verify from a clean machine: npx -y @bicon/mcp-composer@latest starts and composer_version reports the new version. The bundle inlines zod, @workspace/api-zod and @workspace/initiative-model (workspace-only packages the registry cannot resolve), so what ships is dist/index.mjs + this README with @modelcontextprotocol/sdk as the sole runtime dependency — publish-shape.test.ts guards exactly that.

Not in scope (Phase 2)

No authentication, no live POST /initiatives, no read/list/update tools, no hosted/HTTP transport. Those require an API-key auth path in the API server that doesn't exist yet — deliberately deferred to a separate, security-reviewed phase after the production Clerk split.