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

skilled-plus4u-mcp

v1.2.0

Published

MCP server with secure Plus4U authentication and dynamic skill execution

Readme

skilled-plus4u-mcp

MCP server with secure Plus4U authentication and on-demand skill execution.

Features

  • Secure token storage: Tokens stored in OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service)
  • Agent-driven skill discovery: The agent (Claude Code, Cursor, Codex, …) discovers SKILL.md and skill.js itself;
  • Token injection: Skills receive an authenticated HTTP client without ever seeing the token
  • OIDC authentication: browser login, or headless e-mail + password + MFA that captures a session refreshable for ~12 hours
  • Local web console: the wiring of every agent with its live instances, the token, the audit journal and a catalog of every skill on the machine - one loopback-only page, with a macOS menu bar item

Installation

npx uu-safe-install
npm run build

Configuration

Add to your MCP configuration (e.g., mcp.json):

{
  "mcpServers": {
    "skilled-plus4u-mcp": {
      "command": "node",
      "args": ["/path/to/skilled-plus4u-mcp/build/index.js"],
      "env": {
        "LOG_LEVEL": "info"
      }
    }
  }
}

Optional shared HTTP server

Stdio remains the default. HTTP is an optional way to share one server across local clients.

1. Start the server once

skilled-plus4u-mcp http

To sign in from Claude Code, Codex or Cursor, enable OAuth:

skilled-plus4u-mcp http --auth oauth

From this package's source directory:

npm run build
node build/index.js http

Append --auth oauth to the last command to enable the same sign-in flow from source.

The server stays running in that terminal. For background or login startup, register this same command in your preferred process manager and enable it there. Use absolute paths to Node and build/index.js when the process manager does not provide your shell's PATH. Register one server process for all clients. Use Ctrl+C or SIGTERM to stop it.

2. Add the URL to your clients

In a client's MCP settings, choose Streamable HTTP, enter the URL below and enable the connection:

http://127.0.0.1:7391/mcp

The default local mode needs no headers or separate registration command in this package. In OAuth mode, use the same URL without headers, then authenticate as described below.

Claude Code (configuration reference):

claude mcp add --transport http --scope user skilled-plus4u-mcp http://127.0.0.1:7391/mcp

With OAuth enabled, open /mcp, select this server and choose Authenticate. Recent Claude Code versions also support claude mcp login skilled-plus4u-mcp.

Cursor: add this entry to ~/.cursor/mcp.json for all projects, or .cursor/mcp.json for one project (configuration reference):

{
  "mcpServers": {
    "skilled-plus4u-mcp": {
      "url": "http://127.0.0.1:7391/mcp"
    }
  }
}

With OAuth enabled, open Cursor's MCP settings and complete the server's authentication prompt. Client registration is automatic; no auth.CLIENT_ID or client secret is needed.

Codex: add this entry to ~/.codex/config.toml (configuration reference):

[mcp_servers.skilled-plus4u-mcp]
url = "http://127.0.0.1:7391/mcp"

With OAuth enabled, use the app's MCP sign-in action or run:

codex mcp login skilled-plus4u-mcp

This requires a Codex version with HTTP MCP and OAuth support. If codex mcp --help has no login command, that CLI is too old; the desktop app may have a newer runtime than the CLI in your PATH.

Merge the entry into an existing configuration rather than replacing the whole file. In clients you switch to HTTP, replace or disable their previous stdio entry for this server, including a plugin's entry if it supplies one. Otherwise those clients will keep starting additional stdio processes. Reconnect or restart the selected clients. Other clients can continue using stdio.

Signing in with OAuth

The client opens a local consent page. Check the client name and callback address, then choose Allow access and continue. If Plus4U authentication is needed, the existing browser login opens, including its normal OTP or other verification. After sign-in, the browser returns to the client. An existing valid Plus4U sign-in is reused. Each client still needs its own consent.

For trusted apps on your own computer, you can skip this page:

skilled-plus4u-mcp http --auth oauth --consent local

When a client uses an HTTP callback on localhost, 127.0.0.1 or ::1, sign-in starts directly and returns to the client automatically. An existing Plus4U sign-in is reused; otherwise the normal Plus4U browser login opens, including any required OTP. Client configuration stays the same. In the console, choose Setup → shared HTTP → settings → Client approval → Direct login for local apps, then use the updated server command after restarting the server.

This mode trusts local apps without individual approval; it does not verify their names or executables. Other local software or OS users may register a new client and sign in the same way. Revoking an existing grant does not prevent a trusted local client from authorizing again in this mode. External callbacks and cross-site browser navigation still show the consent page. The default --consent required retains individual approval. PKCE, registered callback validation and token checks apply in both modes.

Clients receive separate MCP access and refresh tokens. This OAuth exchange does not send them the Plus4U token, password or OTP. Access allows all tools, including local skill execution under the server's OS user. It does not sandbox skills or isolate them from other same-user processes. The shared Plus4U identity is checked when using or refreshing an MCP token; changing that identity requires new authorization. The server does not switch between different users for different clients.

Access tokens last one hour. Refresh tokens rotate and the authorization lasts at most seven days or 128 refreshes. Reusing a consumed refresh token revokes that authorization. Expired Plus4U authentication can require browser sign-in sooner. Client registrations and token hashes survive a server restart in http-oauth-<port>.json inside SKILLED_STATE_DIR; raw MCP tokens are not stored there. Pending consent requests and authorization codes expire and are discarded on restart.

OAuth-capable clients can revoke their own authorization through the advertised revocation endpoint. Clearing credentials in a client may only remove its local copy; it does not log the shared server out of Plus4U. The existing Plus4U logout affects all clients using that shared identity.

This uses standard OAuth discovery, Dynamic Client Registration, authorization codes with S256 PKCE, refresh and revocation. No client-specific server mode is needed. These instructions target clients running on the same computer; hosted web or cloud agents cannot reach your computer's loopback URL. If Authenticate reports a registration 404, check that this server was started with --auth oauth.

Options and operation

You can prepare both commands and client configuration in the web console:

  1. Open Setup, select Claude Code, Cursor or Codex, then choose shared HTTP in the server row.
  2. In that row's settings, select the port, OAuth or None, and whether to start the web console.
  3. Copy Start the shared server and run it once, or use it in your operating system's service manager.
  4. Copy the client Snippet into the indicated file. Select another client to generate its snippet for the same URL.

The builder keeps stdio as the default. For HTTP it initially selects OAuth and explicitly includes --auth oauth in the startup command; the CLI's bare http command still defaults to no OAuth. Server variables, including proxy configuration, belong to the startup command. Each shared server needs a unique port. Choices are remembered in this browser; changing them prepares new commands and does not reconfigure a running process or edit client files. Imported HTTP entries whose authentication cannot be determined require you to select it before generating a startup command. The optional --access-file mode is configured manually, outside the builder.

| Option | Effect | |--------|--------| | --port 7392 | Use another local port; update the client URL to match. | | --no-console | Skip automatic startup of the existing shared web console. | | --auth oauth | Enable client sign-in and consent through the existing Plus4U browser login. | | --consent local | With OAuth, sign in trusted local apps directly, without the consent page. Default: required. | | --auth none | Keep the default local mode without OAuth. | | --access-file /absolute/path/to/key | Require a Bearer key from an existing private file. |

HTTP runs as an ordinary host process and listens only on 127.0.0.1. Multiple clients share one authentication service and skill cache. Existing Plus4U login, OTP, marketplace and Skill Hub tools remain available. Log in through the console or skilled-plus4u-mcp login as before. KNOW_HOW_SET_OID still selects proxy mode, with SERVER_URL selecting its upstream; differently configured proxies need different ports. The web console remains a separate shared process.

Without --auth oauth or --access-file, other local processes can call the MCP tools, including skill execution under the server's OS user. Host and Origin checks block foreign web origins; they do not isolate local users or applications. Use the optional key on a shared computer. Its file must contain 43-128 letters, digits, underscores or hyphens and have mode 600 on macOS/Linux. Configure the client header Authorization: Bearer <contents-of-key-file>. The server never generates or prints the key. Restart the server after replacing it and update the authorized clients. This key is separate from Plus4U login. OAuth and --access-file are alternative access modes and cannot be combined.

GET http://127.0.0.1:7391/health returns the PID and active request count; it uses the same optional authentication. Diagnostics go to stderr. A port conflict means another process is already listening: reuse that server or choose another port. Clients connect to a running server; they do not start it.

Request bodies are limited to 1 MiB and 128 requests can run simultaneously; excess requests receive 503 with Retry-After: 1. MCP contexts are released after each request. The transport supports modern MCP and stateless 2025 Streamable HTTP; legacy GET/DELETE session operations return 405. Skills run inside the shared process, so a blocking or crashing skill affects its HTTP clients. OAuth bodies are limited to 16 KiB, with at most 32 active OAuth requests, 64 pending consents, 128 pending codes, 512 registered clients and 512 authorizations. Registrations expire after 30 days.

Environment Variables

| Variable | Default | Description | |----------|---------|-------------| | LOG_LEVEL | info | Logging level: error, warn, info, debug. Logs go to stderr; stdout is the MCP transport | | KNOW_HOW_SET_OID | - | Binds the server to one uuAiChat knowHowSet and flips it into proxy mode | | SERVER_URL | production uuAiChat | Upstream uuAiChat AWID base or full MCP endpoint URL, proxy servers only | | SKILL_DIRS | - | Kept for agents that still pass it; nothing in the server reads it | | SKILLED_SETTINGS_FILE | ~/.skilled-plus4u-mcp/settings.json | Where this machine's settings file is (see below). A path may start with ~ | | SKILLED_STATE_DIR | ~/.skilled-plus4u-mcp | Where the sign-in session state is kept | | SKILLED_TRUSTED_SERVER_HOSTS | empty | Comma-separated exact hosts or . / *. suffixes allowed for non-Plus4U proxy targets. HTTP is allowed only for an explicitly trusted loopback host | | SKILLED_JOURNAL | on | off / 0 / false disables the local audit journal | | SKILLED_JOURNAL_DIR | ~/.skilled-plus4u-mcp/journal | Journal directory | | SKILLED_JOURNAL_RETENTION_DAYS | 90 | Journal files older than this are deleted on startup | | SKILLED_JOURNAL_DETAIL | summary | How much of what a call returned is kept: off (sizes only), summary, full | | SKILLED_INSTANCES_DIR | ~/.skilled-plus4u-mcp/instances | Where running instances announce themselves for the console | | SKILLED_CONSOLE_PORT | ephemeral | Default port for skilled-plus4u-mcp console | | SKILLED_CONSOLE_AUTOSTART | on | off stops servers from bringing the console (and the menu bar icon) up | | SKILLED_GIT_PATH | PATH | The git used for marketplace clones | | SKILLED_CLAUDE_PATH | PATH | The claude used to install marketplace plugins into Claude Code | | SKILLED_CODEX_PATH | PATH | The codex used to install marketplace plugins into Codex | | SKILLED_PS_PATH | /bin/ps, /usr/bin/ps, PATH | The ps used to read live memory and CPU of instances | | SKILLED_XDG_OPEN_PATH | PATH | The xdg-open used to open the browser on Linux |

Settings file

Every variable above belongs to one server: it is set in that server's entry in the agent's config, and the console's Setup view composes it there, per server, with the values it may take. What every server and the console on the machine share lives in ~/.skilled-plus4u-mcp/settings.json instead: keepAlive (the console renews the token while it runs) and installPluginMcpServers (a marketplace plugin may install its own MCP server). The console's Settings view edits that file; a missing key means the default. The console never edits an agent's config.

Session lifetime

Every sign-in is bounded. The moment you complete an interactive login the session gets a hard deadline 12 hours out; it is stored next to the token and survives restarts. Until then the token is refreshed silently from the OIDC session, so agents never meet a dead token. Once the deadline passes, refreshing stops and the next call asks for a real sign-in again — there is no way to extend a session past 12 hours by refreshing it, and no unattended path back in.

Second factors are entered by a human every time: the console and loginWithStoredCredentials prompt for the authenticator code or a backup code. The authenticator seed is deliberately not storable, because a stored seed would turn the login into something the machine can repeat by itself.

E-mail and password may still be kept in the keychain so the sign-in is two clicks and one code:

# the password is only ever read from stdin, never argv
skilled-plus4u-mcp credentials set --email [email protected]
skilled-plus4u-mcp credentials status

getAuthStatus and skilled-plus4u-mcp status report sessionExpiry alongside sessionHardExpiry — the deadline that cannot move.

Audit journal

Every run appends what the server actually did to ~/.skilled-plus4u-mcp/journal/YYYY-MM-DD.jsonl (directory 0700, files 0600) — one JSON object per line:

| ev | Recorded | |------|----------| | server.start, proxy.start | version, mode, upstream, knowHowSetOid | | auth.login, auth.logout, cli.login, cli.logout, cli.refresh | method, result, uuIdentity | | skill.execute | skill, scriptPath, parameters, result, duration, result size, output file | | skill.batch | the same plus per-item outcome and timing | | proxy.call | forwarded tool name, arguments, result, duration | | http (nested in the skill entry) | method, URL, status, duration, request/response size, and - unless detail is off - what was sent and what came back | | console.* | the dashboard's own actions: sign-ins, keep-alive runs, settings changes, instances it stopped |

The journal is a local record for the machine's owner: there is no MCP tool and no resource exposing it, and no tool description mentions it. Nothing it does can fail the server — every write error is swallowed.

How much is kept

skill.execute, skill.batch and proxy.call record what they were asked for and what came back, so one entry answers "what did it call, and what did it return" on its own. SKILLED_JOURNAL_DETAIL sets how much of the answer survives:

| Level | Returned values | Per-string | Per-array | Depth | Line cap | |-------|-----------------|-----------|-----------|-------|----------| | off | not kept, only resultBytes | - | - | - | 16 KB | | summary (default) | shaped preview | 220 chars | 6 items | 4 | 16 KB | | full | a large slice | 4000 chars | 60 items | 8 | 96 KB |

Truncation is marked rather than silent (…+34 more of 40, …(+680 chars)), and the payload budget is counted from the value's own root, so the depth already spent reaching it does not eat it. Redaction is unchanged and applies at every level: a password, secret or token key inside a returned object is [redacted] in full exactly as in summary.

Secrets never reach the file. Values are dropped for keys containing password, secret, token, apiKey, accessCode, authorization, cookie, totp/otp/mfa, credential, privateKey, sessionId or ending in pat; JWTs, Bearer … values, PEM blocks and sensitive URL query parameters are stripped wherever they appear. BookKit page codes (code) and JIRA issue keys (key) are deliberately kept. Strings are truncated at 300 chars, arrays at 20 items, nesting at 6 levels, and a whole line at 16 KB — so page content and large payloads are summarized, not stored.

MCP Tools

login

Authenticate with Plus4U and store the token in the OS keychain. The default method: "browser" opens OIDC in a browser; method: "credentials" uses credentials already stored in the keychain and requests only the MFA interaction in-band.

logout

Clear authentication token from keychain.

getAuthStatus

Check if authenticated with Plus4U without triggering login. Returns authenticated: true/false.

syncMarketplaces

Synchronize known marketplaces into a shared store and register them with Claude Code, Codex and Cursor. It runs throttled at server startup and installs offered plugins by default; use listOnly: true for discovery only or install: false to skip installation. Sync only fast-forwards clean, behind-only clones. Dirty, ahead, diverged or origin-mismatched clones are preserved and reported, and existing agent registrations are not re-pointed unless takeover: true is explicit.

The console is deliberately not an MCP tool - like the journal, it is for the machine's owner, not for the agent.

executeSkill

Execute a skill script identified by absolute path.

{
  "scriptPath": "/Users/me/.claude/skills/bookkit-read/skill.js",
  "params": {
    "url": "https://uuapp.plus4u.net/uu-bookkit-maing01/AWID/book/page?code=home"
  }
}

The agent is responsible for resolving scriptPath from its own SKILL.md discovery (Claude Code skills, Cursor rules, Codex skills, …). Convention: skill.js co-located with SKILL.md in the same directory.

batchExecuteSkill

Execute a skill script multiple times in parallel.

{
  "scriptPath": "/Users/me/.claude/skills/bookkit-read/skill.js",
  "paramsList": [
    { "url": "…/page1" },
    { "url": "…/page2" }
  ]
}

Proxy mode — one MCP server per uuAiChat knowHowSet

The same skilled-plus4u-mcp binary doubles as a uuAiChat MCP proxy. Set KNOW_HOW_SET_OID in its environment and it stops registering its skill tools — instead it runs a thin stdio↔HTTP MCP proxy bound to that single knowHowSet. Add several entries to your agent config, one per knowHowSet, and your agent sees them as independent MCP servers.

The proxy reuses the Plus4U token from the shared OS keychain (same entry as the regular skilled-plus4u-mcp and as urc-cli). You log in once and every proxy instance picks the token up — no copy-pasting tokens into MCP configs, no manual refresh.

How it works

Claude Code / Cursor                                         uuAiChat
       │                                                        │
       │  stdio JSON-RPC                                        │
       │  ─────────────▶  skilled-plus4u-mcp (proxy mode)       │
       │                       │                                │
       │                       │  POST <SERVER_URL>             │
       │                       │  Authorization: Bearer <tok>   │
       │                       │  body adds knowHowSetOidList:  │
       │                       │    [KNOW_HOW_SET_OID]          │
       │                       ─────────────────────────────▶   │
       │                       ◀─────────────────────────────   │
       │                       │  on 401/403:                   │
       │                       │    silent OIDC refresh + retry │
       │  ◀────────────                                         │
  1. When KNOW_HOW_SET_OID is set, skilled-plus4u-mcp runs runProxy() in src/proxy.ts instead of the regular skill-tool registration.
  2. tools/list is fetched lazily from upstream on the first request and cached for the lifetime of the process.
  3. Every tools/call gets { knowHowSetOidList: [<your OID>] } merged into its arguments before being forwarded.
  4. On HTTP 401/403 the proxy calls AuthService.refreshSession() (silent OIDC refresh against the cached ~12 h session cookies) and retries the request once. If silent refresh fails, the proxy returns a clear "Not authenticated" error.

Setup

  1. Log in once. Use either:

    • the login tool of a regular (non-proxy) skilled-plus4u-mcp instance, or
    • urc auth login / urc auth login --credentials from urc-cli

    Both write to the same OS keychain entry (service skilled-plus4u-mcp, account default-user). The browser flow stores only the access token; --credentials additionally captures session cookies that enable silent refresh for ~12 hours.

  2. Configure one MCP server per knowHowSet in your agent config:

    {
      "mcpServers": {
        "aichat-customer-support": {
          "command": "npx",
          "args": ["-y", "skilled-plus4u-mcp"],
          "env": { "KNOW_HOW_SET_OID": "62a1c8f3...c0" }
        },
        "aichat-product-docs": {
          "command": "npx",
          "args": ["-y", "skilled-plus4u-mcp"],
          "env": { "KNOW_HOW_SET_OID": "9f87bb01...e3" }
        }
      }
    }

    Until the package is published to Plus4U Nexus, replace "command": "npx" / "args": ["-y", "skilled-plus4u-mcp"] with an absolute path to your local build, e.g.:

    "command": "node",
    "args": ["/abs/path/to/skilled-plus4u-mcp/build/index.js"]

Environment variables

| Variable | Required | Default | Description | |----------|:---:|---------|-------------| | KNOW_HOW_SET_OID | yes | — | The knowHowSet OID this instance binds to. Its presence flips skilled-plus4u-mcp into proxy mode. Injected as knowHowSetOidList: [<oid>] into every forwarded tools/call (no need to pass it again in tool arguments). | | SERVER_URL | no | production uuAiChat (see below) | Upstream uuAiChat AWID base or full MCP endpoint URL. MCP_SERVER_URL is also accepted for backward compat. | | SKILLED_TRUSTED_SERVER_HOSTS | no | empty | Additional exact hosts or . / *. suffixes trusted for SERVER_URL. Plus4U hosts are trusted by default; HTTP additionally requires an explicitly trusted loopback host. | | LOG_LEVEL | no | info | error, warn, info, debug. Logs go to stderr (stdout is reserved for the MCP transport). |

SERVER_URL — what to put there

The proxy is happy with two shapes of URL and normalizes them transparently:

  • AWID base — e.g. https://uuapp.plus4u.net/uu-aichat-maing01/4f6642c05388ad5df24b235b61a67414 Detected by the trailing 32-hex AWID. The proxy auto-appends the uuAiChat MCP use-case /businessChat/mcp before sending.
  • Full MCP endpoint URL — e.g. https://uuapp.plus4u.net/uu-aichat-maing01/<awid>/businessChat/mcp Used as-is. Useful if uuAiChat ever exposes a non-default use-case.

Default (when neither SERVER_URL nor MCP_SERVER_URL is set):

https://uuapp.plus4u.net/uu-aichat-maing01/4f6642c05388ad5df24b235b61a67414

i.e. the production uuAiChat instance. After normalization the actual upstream becomes …/4f66…7414/businessChat/mcp.

Override SERVER_URL when:

  • you want to target a different uuAiChat AWID (test, sandbox, customer-specific instance)
  • uuAiChat introduces a different use-case path in the future — point at the full URL

Example with a custom uuAiChat:

"env": {
  "KNOW_HOW_SET_OID": "62a1c8f3...c0",
  "SERVER_URL": "https://uuapp.plus4u.net/uu-aichat-maing01/<other-awid>"
}

Local development / manual run

Inside the repo:

cd mcp/skilled-plus4u-mcp
npx uu-safe-install
npm run build
KNOW_HOW_SET_OID=62a1...c0 node build/index.js   # proxy mode
node build/index.js                              # regular skill server

Useful diagnostic env: LOG_LEVEL=debug exposes the upstream URL, auth refresh attempts, and tool counts.

Troubleshooting

| Symptom | Likely cause | Fix | |---------|--------------|-----| | HTTP 404: Not Found from upstream | SERVER_URL doesn't resolve to a uuAiChat MCP endpoint | Verify the AWID; check the URL ends with /businessChat/mcp after normalization (set LOG_LEVEL=debug and read the started (upstream: …) line). | | HTTP 401/403 after a retry | OIDC session expired (>12 h since last interactive login) | Run login (browser flow) or urc auth login --credentials again to refresh both token and session cookies. | | Not authenticated. Run the login tool… | Keychain has no valid token and no usable session | Same as above — re-run login. | | Proxy starts but tools/list returns nothing | KNOW_HOW_SET_OID doesn't match anything on that uuAiChat AWID | Verify OID; try a different SERVER_URL if the knowHowSet lives in a different instance. | | Multiple proxies, only one of them works | Each instance maintains its own in-memory tools/list cache — that's expected | None — restart the failing instance once auth is fixed. |

Web console

Every agent - Claude Code, Cursor, Codex, one process per uuAiChat knowHowSet - spawns its own stdio instance, so there is no single server to ask what is going on. What they share lives outside any of them: the keychain token, the instance registry and the audit journal. The console is a separate local process that reads those three, renders them, and keeps the token alive while it runs.

skilled-plus4u-mcp console              # random free port, opens the browser, puts an icon in the menu bar
skilled-plus4u-mcp console --port 7390  # pin the console port (separate from HTTP MCP)
skilled-plus4u-mcp console --no-open    # just print the URL
skilled-plus4u-mcp console --no-tray    # no menu bar item

It comes up on its own. The first server that starts on this machine spawns the console detached if none is running, so the menu bar icon is simply there once any agent is up. Set SKILLED_CONSOLE_AUTOSTART=off to prevent that. Concurrent starts (an agent launching six servers at once) race for an exclusive lock file, so exactly one console is spawned.

| View | Shows | |------|-------| | Servers | running servers with process counts, tools, memory and activity. Shared HTTP clients are grouped under one endpoint; stdio processes remain grouped by their client configuration. Each detail shows the server configuration and its individual processes, with resource usage, calls, tools and log links. Configurations with no running process are in the expandable Not running section. The console and unassigned processes remain visible. | | Access | the token and session gauges, step-by-step credentials sign-in with MFA, the keep-alive switch | | Settings | the settings file: what every server and the console share, one row per key with its value and a reset. Variables belong to a server and are set on Setup | | Setup | stdio or shared HTTP configuration, port and OAuth selection, server startup commands, and client snippets for Claude Code, Cursor and Codex | | Skills | every skill, slash command, rule and agent definition on this machine - user directories, marketplace repos, the Cursor extension's synced copies and the installed-plugin cache - with its type and description, which plugin and marketplace it came from, whether it has an executable script, and how often it has actually run | | Markets | every plugin marketplace this machine can see, from all three places they live, searchable across marketplaces by plugin, keyword, tag and category. A marketplace opens as a page: remote, branch, documentation book, install state per agent, its plugins in a table or as tiles with version, category, artifact counts and keywords, and the repository's changelog and README rendered. A plugin opens one level deeper: where it is installed (Claude Code, Cursor, Codex) against the repository version, its rules with how they apply, skills with their call counts, commands, agents, MCP servers with their placeholders, changelog and README. Browsing only - installing stays with syncMarketplaces | | Graph | every skill as a node and every SKILL.md that names another skill as an edge - sized by links, coloured by marketplace (or source / plugin), orphans faded; filters and group toggles, an Obsidian-style hover focus, each node opening the Skills drawer, which in turn shows the skill's own neighbourhood | | Context | the instruction stack for one location and one agent - the user file, every CLAUDE.md / AGENTS.md / GEMINI.md on the way down from the root, the rules folder, @imports, Cursor rules by front matter - with what is loaded on every prompt and what only on demand, the size in bytes and tokens, headings defined twice, what the other agents see at the same place, and suggestions (a project Codex is blind to, imports pointing nowhere, a chain over Codex's 32 kB cap). A second mode lists every instruction file on the machine | | Cleanup | what the agents load from their global paths, as findings: copies that drift (with a line diff between any two), one agent holding the same thing twice, empty scripts and SKILL_DIRS entries pointing nowhere, artifacts nothing runs or references, things one agent has and the others lack (copy them over). Removal moves into ~/.skilled-plus4u-mcp/trash/<date>/, never deletes; plugin-cache copies are left to the agent | | Activity | the audit journal, filtered by day, event type, instance (sid) or free text. A row opens a panel: what happened and how long it took, which instance and agent ran it, what it was asked for, what it returned, and every Plus4U call it made - each expanding to the request and the response |

Each HTTP endpoint has one row, so its processes, memory and activity are counted once. Client badges and the detail list show configurations using that endpoint, not currently connected clients. The header counts MCP processes and their memory separately from the console. HTTP and stdio can appear together, including old stdio processes still running after a client switches to HTTP.

The detail panel opens on the right at 660 px, can be dragged wider by its left edge (the width is remembered per browser) and expands to full reading width with the button in its header - at which point the detail lays itself out in two columns. Esc closes it.

Signing in, and the 12 hour token

The browser flow stores an access token and nothing else, so roughly an hour later every skill fails with "Not authenticated". Credentials sign-in (e-mail + password + MFA, right in the console) captures the OIDC session cookies as well, and those are what make the token refreshable — up to the 12 hour deadline the sign-in was given, never beyond it.

Refresh while the console runs (on by default) means that once a minute the console renews the token from the session before it gets close to expiring. Since the token lives in the shared keychain, every instance picks it up, so with the console in the menu bar no agent meets a dead token during those 12 hours. When the deadline arrives the refreshing stops and the console asks for a fresh sign-in; there is no unattended way around it.

The password is posted to the loopback console and handed straight to the OIDC flow; it is kept only if "keep credentials in the keychain" is ticked, and the journal's redaction keeps it (and every one-time code and token) out of the audit file.

Skills catalog

The server registers no skills and never will: it exposes executeSkill and runs the script path the agent hands it. That was a deliberate simplification - the earlier listSkills / reloadSkills tools scanned SKILL_DIRS and require()d every .js they found, so one skill calling process.exit() at import time took the whole server down with it. SKILL_DIRS survives in agent configs but nothing in the server reads it.

What was lost with it is any way to see what exists. The Skills view scans for SKILL.md - ~/.claude/skills, ~/.cursor/skills, ~/.codex/skills, ~/.gemini/skills, ~/.claude/plugins/marketplaces/** and ~/.claude/plugins/cache/**, plus every path any agent or running instance names in SKILL_DIRS - and joins the result with the journal, which knows what has been executed. For each skill it shows the description from the front matter, the marketplace or plugin it came from (newest version kept, older ones noted), whether a skill.js is actually there, the calls and failures of the last seven days, and the exact scriptPath to hand to executeSkill.

It only ever reads files - front matter and a directory listing. Nothing is imported, so the crash that killed the old loader cannot happen here. Two things it surfaces that were previously invisible: skills whose skill.js is missing (documentation-only, marked doc), and SKILL_DIRS entries pointing at directories that no longer exist.

Marketplaces

Marketplaces exist in three parallel worlds on one machine, and each tool shows only its own:

  • the repos you develop in - anything under your home with a .claude-plugin/marketplace.json or .cursor-plugin/marketplace.json (found by manifest, not by a -mp name, so usy_smarta_marketplaceg01 counts too)
  • the Cursor extension's synced copies - it keeps a full clone of every registered marketplace under its globalStorage/…/git-cache, plus a registry of what is installed where
  • the marketplaces Claude Code registered for itself under ~/.claude/plugins/marketplaces, with installed plugin versions in ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>

The Markets view is those three joined into one list, deduplicated by marketplace name, with the copies it was found in shown per row. Each marketplace opens to its manifest (version, owner, description), the git state of the clone (remote, branch, uncommitted count, last commit), the extension's last sync and synced commit - and its plugins, each with the version in the repo, how many skills it holds, how many of those are actually runnable, and where it is installed. When the repo version and the installed version disagree, the plugin is flagged as out of date: the case you otherwise only notice by wondering why a fix you committed has no effect.

A plugin in that list opens one level deeper: its manifests (and which of claude / cursor / codex it carries one for), author, licence and keywords, then everything it actually ships - skills with their descriptions and whether each has a runnable script, slash commands, rules, agents, hooks, and the MCP servers it would install (their command and env, with secrets redacted). Its README is there too. A skill in that list jumps to the Skills catalog entry for it, which is the place that knows how often it has been run.

Every marketplace copy also becomes a root for the skill scan, which is why the Skills catalog covers skills you have never installed - they are visible in the repo or in the extension's sync, labelled <plugin> · <marketplace>.

Menu bar / notification area

console also starts a small helper that puts an item in the macOS menu bar showing the time left on the token, with Open console, Sign in and Quit console. It is a plain JXA script run by osascript - nothing to install, no native module, no Electron. The helper reads one status file the console refreshes and exits by itself once the console is gone. The Windows counterpart (tray.ps1, a NotifyIcon driven by powershell) is written to the same contract but is untested; on Linux the console simply runs headless.

Instance registry

Each process writes ~/.skilled-plus4u-mcp/instances/<pid>-<sid>.json (mode 0700/0600) at startup, refreshes it every 30 s and removes it on exit; a record whose pid is gone is deleted by the next read. The sid is the same one the journal stamps on every line, which is how the console attributes calls, failures and timings to a specific instance without any extra logging. Live memory and CPU come from ps at read time, so the numbers are current rather than as of the last heartbeat.

Security

  • binds to 127.0.0.1 only, and refuses requests whose Host is not loopback
  • a fresh random token per run: the page is served only with it in the URL, every /api/* call must carry it as Authorization: Bearer … - a page on another localhost port cannot forge that header
  • the token is stripped from the address bar after load, and the page is served under a CSP that blocks every external request

Writing Skills

Skills are JavaScript modules exporting execute:

async function execute(params, http) {
    const result = await http.get('https://api.example.com/data');
    return result;
}

module.exports = { execute };

The server caches loaded modules per absolute path and invalidates the cache when the script's mtime changes — no reloadSkills tool needed.

SKILL.md ↔ skill.js linking

The agent learns about a skill through its own SKILL.md. To run that skill, the agent passes the absolute path to the executable script (typically skill.js co-located with SKILL.md) to executeSkill. This server has no opinion about how the agent finds those files.

Security

  • Skills never see the token — they only receive the http object
  • http.get() / http.post() auto-add the Bearer token
  • Token is stored in OS keychain, not in env vars or files

License

MIT