Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 3
versioning-strategy: increase
groups:
development:
dependency-type: development
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 2
14 changes: 8 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,25 @@ jobs:
node: [20.19.0, 22.x, 24.x]

steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run check:dependencies
- run: npm ci --ignore-scripts --no-audit
- run: npm run check

package:
name: MCPB package
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: 22.x
cache: npm
- run: npm ci
- run: npm run check:dependencies
- run: npm ci --ignore-scripts --no-audit
- run: npm run test:package
8 changes: 4 additions & 4 deletions .github/workflows/dependency-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: 22.x
cache: npm
- run: npm ci
- run: npm audit --audit-level=low
- run: npm run check:dependencies
- run: npm audit --package-lock-only --ignore-scripts --audit-level=low
3 changes: 2 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
save-exact=true
save-exact=true
ignore-scripts=true
2 changes: 1 addition & 1 deletion NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Thomas Main began the independent modernization in August 2026. Modified source

The complete project is distributed under GPL-2.0-only. See `LICENSE` for the license terms. Published packages include the corresponding TypeScript source and build scripts.

The generated OBS protocol data is based on `obsproject/obs-websocket` commit `1ef34bf48110c2a18184e50e41cd0b1a855e2147`, the revision pinned by the OBS Studio baseline documented in the README.
The generated OBS protocol data is based on `obsproject/obs-websocket` commit `e28f84975984340e84fc4879cfc05741cc0afde1`, the revision pinned by the OBS Studio baseline documented in the README.

Upstream references:

Expand Down
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,20 +320,36 @@ The suite refuses to mutate if the selected collection does not exactly match `O

Add `OBS_MCP_LIVE_RECORD=1` to the mutation command to also record a one-second clip. It checks that `obs-start-record` reports success only after OBS confirms the output is active, then stops the recording in a `finally` block. The clip is saved to the profile's recording directory and captures whatever the selected collection shows, so select a collection whose sources are safe to record.

Pull-request CI runs the hermetic gate on Node 20.19, 22, and 24, plus the packaged-artifact smoke test on Node 22. Dependency auditing runs in a separate weekly workflow so registry advisories do not make otherwise reproducible pull-request checks flaky.
Pull-request CI checks the dependency policy before installation, then runs the hermetic gate on Node 20.19, 22, and 24, plus the packaged-artifact smoke test on Node 22. Dependency auditing checks the lockfile in a separate weekly workflow so registry advisories do not make otherwise reproducible pull-request checks flaky.

### Dependency safety

Direct dependencies use exact versions. `npm run check:dependencies` checks that the lockfile matches the manifest, uses npm registry HTTPS tarballs with SHA-512 integrity, and contains no production dependency install scripts. The check runs offline and requires no installed packages:

```bash
node scripts/check-dependencies.mjs
```

The project disables dependency lifecycle scripts through `.npmrc`; the MCPB staging install also passes `--ignore-scripts` explicitly. Explicit commands such as `npm run build` still run. Dependabot proposes weekly npm and GitHub Actions updates for review. CI actions are pinned to verified commit hashes. The residual development advisory and published SDK license findings are recorded in [`docs/dependency-security.md`](docs/dependency-security.md).

For a dependency update, inspect its release notes and changes to the lockfile, retain exact direct versions, then run `npm run check` and `npm run test:package`. Check current advisories with `npm audit --package-lock-only --ignore-scripts --audit-level=low`. An audit reports known registry advisories; the offline policy check validates the dependency graph's packaging constraints.

Vitest remains on 4.1.11 because Vitest 5 drops Node 20 support. Node type declarations stay on the supported Node 22 line.

### TypeScript policy

The application builds with TypeScript 7.0.2 and does not import the TypeScript compiler API. If a future generator, linter, or build tool needs programmatic compiler access, keep that tool on TypeScript 6 while the application compiler remains on TypeScript 7. TypeScript 7.0 does not ship a programmatic API; Microsoft documents the side-by-side TypeScript 6 compatibility package in the [TypeScript 7.0 release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#running-side-by-side-with-typescript-6-0).

## Protocol baselines

This revision was checked against these exact sources:
On October 14, 2026, this revision was compared with the `modelcontextprotocol`, `mcp-typescript-sdk`, and `obs-studio` checkouts under `/Volumes/External SSD/referencerepos`. Those checkouts were read without modification. The exact reference sources are:

- MCP specification: [`5ea99602`](https://github.com/modelcontextprotocol/modelcontextprotocol/commit/5ea996028df322ce97d1c906731cf8c05a6e268e)
- MCP TypeScript server and client SDKs: `2.3.1`; reference checkout [`9bc40e90`](https://github.com/modelcontextprotocol/typescript-sdk/commit/9bc40e903fde15534ec06ff4c1347d47000dbe16)
- OBS Studio: [`f082210c`](https://github.com/obsproject/obs-studio/commit/f082210c177a03572cace88c168d0f32d8cde39e)
- OBS WebSocket: [`e28f8497`](https://github.com/obsproject/obs-websocket/commit/e28f84975984340e84fc4879cfc05741cc0afde1), the revision pinned by that OBS Studio checkout

- MCP specification: [`cbd57657`](https://github.com/modelcontextprotocol/modelcontextprotocol/commit/cbd57657ec769b942263a5251afc05959fc4ce58)
- MCP TypeScript server and client SDKs: `2.0.0`
- OBS Studio: [`0043697f`](https://github.com/obsproject/obs-studio/commit/0043697fc59d791b95b3495dfa1fe180b395966f)
- OBS WebSocket: [`1ef34bf4`](https://github.com/obsproject/obs-websocket/commit/1ef34bf48110c2a18184e50e41cd0b1a855e2147), the revision pinned by that OBS Studio checkout
The refreshed OBS WebSocket revision has the same 147 requests, 60 events, and 7 enums as the previous baseline; its protocol JSON is byte-for-byte identical. MCP 2026-07-28 remains the released protocol baseline.

The generated protocol files live under [`docs/`](docs/). Tests compare every request used by the tool modules with the bundled protocol, so obsolete request names and missing wrappers fail locally.

Expand Down
42 changes: 42 additions & 0 deletions docs/dependency-security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Dependency security assessment

Checked on October 14, 2026. This records a remaining dependency advisory, not an audit exemption.

## Runtime license constraint

The project is GPL-2.0-only. Review of the published MCP 2.0, 2.1, and 2.2 tarballs found the same mixed LICENSE text involving Apache-2.0 despite their MIT package metadata. SDK 2.3 synchronizes its package metadata with that existing license text; it does not introduce this distribution concern. Apache's [GPL compatibility guidance](https://www.apache.org/licenses/GPL-compatibility) explains the GPLv2 incompatibility.

Returning to an earlier SDK version does not resolve the published-license discrepancy. This pass updates the source integration to SDK 2.3.1 and tests a local artifact; it does not change the project license or publish a package.

The GPL choice follows the inherited `royshil/obs-mcp` implementation. This fork introduced explicit GPL-2.0-only metadata and source notices in August 2026. OBS Studio and OBS WebSocket explicitly permit GPLv2 or later, so their licensing alone does not establish a GPLv2-only restriction. Roy's upstream ships GPLv2 license text without a separate clear later-version grant; the sample later-version wording in the license appendix is not a project-specific grant. Establish the applicable inherited permissions before changing the combined project to GPLv3 or claiming MIT availability. This is a license-alignment question for redistribution, not a blocker on source integration or private local verification.

The offline dependency guard checks pinned versions, registry sources, integrity, lockfile correspondence, and installation hooks. License compatibility requires a separate review of actual license documents; package metadata and version numbers are insufficient here. Development tools and files included in the source distribution also require their own assessment.

## Remaining advisory

The development dependency path `@anthropic-ai/mcpb@2.1.2 → node-forge@1.4.0` remains affected by high-severity [GHSA-86w9-cpqp-85rv / CVE-2026-85393](https://github.com/advisories/GHSA-86w9-cpqp-85rv). The advisory covers versions through 1.4.0 and lists no patched version. It concerns RSA PKCS#1 v1.5 signature verification accepting malformed nested DigestAlgorithm elements, which can enable signature forgery with low-exponent RSA keys.

The installed lockfile marks both packages as development dependencies. Neither is part of the server's production dependency graph. Packaging installs production dependencies with `npm ci --omit=dev --ignore-scripts --no-audit`; the MCPB tool itself runs from the development checkout.

## Reachability in this project

These findings are based on the installed `@anthropic-ai/mcpb@2.1.2` source and the project's current call sites. They describe this workflow, not every application of node-forge.

| Project operation | MCPB call path | Relevant behavior |
| --- | --- | --- |
| `npm run pack` | CLI `pack` → `packExtension` → `validateManifest` | Validates the local manifest and icon, collects local files, and creates a ZIP. It does not sign the archive or verify RSA signatures. |
| `npm run test:package` | `unpackExtension`, then `validateManifest` | Extracts the locally built artifact. Signature-footer extraction uses Buffer operations; it does not authenticate the signature or call forge verification. |
| `npm run validate:manifest` | CLI `validate` → `validateManifest` | Parses JSON, applies the manifest schema, and checks the local icon. It does not verify signatures. |
| Production MCP server | `src/index.ts` and server modules | No import or invocation of MCPB or node-forge was found. |

MCPB imports its signing module, which imports node-forge, even when these operations do not call its cryptographic functions. Importing the package therefore does not establish that the vulnerable verification operation executes. No project invocation of `signMcpbFile` or `verifyMcpbFile` was found.

An additional limitation exists in the installed verifier: MCPB's `verifyMcpbFile` calls `p7.verify`, but node-forge 1.4.0's [PKCS#7 verifier](https://github.com/digitalbazaar/forge/blob/v1.4.0/lib/pkcs7.js) throws because verification is unimplemented. MCPB catches that failure and reports the file as unsigned. This is not a patch for the RSA advisory, and the MCPB verifier should not be treated as a working authenticity boundary.

## Mitigation and follow-up

Keep the development tool out of production installations and build artifacts, keep dependency lifecycle scripts disabled, and restrict the package smoke test to the artifact built by this checkout. The current workflow does not depend on MCPB signature verification to decide whether an external bundle is trusted.

These measures limit exposure; they do not repair node-forge or make the complete development graph vulnerability-free. The weekly audit continues to report the advisory without suppression. Recheck upstream releases and replace the affected development dependency when a reviewed fix becomes available. Any new signing or verification workflow requires a fresh assessment and a functioning independent authenticity mechanism before accepting external bundles.

Read-only verification confirmed the development-only lockfile classification, exercised `validateManifest` without entering forge signing/RSA-key construction, and confirmed that the installed PKCS#7 verifier throws. Reachability for packing and unpacking was assessed from their source paths; this assessment is not an exploit proof or a cryptographic audit.
22 changes: 11 additions & 11 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -2663,7 +2663,7 @@ communication is desired.

## Requests Table of Contents

- [General Requests](#general-1-requests)
- [General Requests](#general-requests)
- [GetVersion](#getversion)
- [GetStats](#getstats)
- [BroadcastCustomEvent](#broadcastcustomevent)
Expand All @@ -2672,7 +2672,7 @@ communication is desired.
- [TriggerHotkeyByName](#triggerhotkeybyname)
- [TriggerHotkeyByKeySequence](#triggerhotkeybykeysequence)
- [Sleep](#sleep)
- [Config Requests](#config-1-requests)
- [Config Requests](#config-requests)
- [GetPersistentData](#getpersistentdata)
- [SetPersistentData](#setpersistentdata)
- [GetSceneCollectionList](#getscenecollectionlist)
Expand All @@ -2694,9 +2694,9 @@ communication is desired.
- [GetSourceActive](#getsourceactive)
- [GetSourceScreenshot](#getsourcescreenshot)
- [SaveSourceScreenshot](#savesourcescreenshot)
- [Canvases Requests](#canvases-1-requests)
- [Canvases Requests](#canvases-requests)
- [GetCanvasList](#getcanvaslist)
- [Scenes Requests](#scenes-1-requests)
- [Scenes Requests](#scenes-requests)
- [GetSceneList](#getscenelist)
- [GetGroupList](#getgrouplist)
- [GetCurrentProgramScene](#getcurrentprogramscene)
Expand All @@ -2708,7 +2708,7 @@ communication is desired.
- [SetSceneName](#setscenename)
- [GetSceneSceneTransitionOverride](#getscenescenetransitionoverride)
- [SetSceneSceneTransitionOverride](#setscenescenetransitionoverride)
- [Inputs Requests](#inputs-1-requests)
- [Inputs Requests](#inputs-requests)
- [GetInputList](#getinputlist)
- [GetInputKindList](#getinputkindlist)
- [GetSpecialInputs](#getspecialinputs)
Expand Down Expand Up @@ -2737,7 +2737,7 @@ communication is desired.
- [SetInputDeinterlaceFieldOrder](#setinputdeinterlacefieldorder)
- [GetInputPropertiesListPropertyItems](#getinputpropertieslistpropertyitems)
- [PressInputPropertiesButton](#pressinputpropertiesbutton)
- [Transitions Requests](#transitions-1-requests)
- [Transitions Requests](#transitions-requests)
- [GetTransitionKindList](#gettransitionkindlist)
- [GetSceneTransitionList](#getscenetransitionlist)
- [GetCurrentSceneTransition](#getcurrentscenetransition)
Expand All @@ -2747,7 +2747,7 @@ communication is desired.
- [GetCurrentSceneTransitionCursor](#getcurrentscenetransitioncursor)
- [TriggerStudioModeTransition](#triggerstudiomodetransition)
- [SetTBarPosition](#settbarposition)
- [Filters Requests](#filters-1-requests)
- [Filters Requests](#filters-requests)
- [GetSourceFilterKindList](#getsourcefilterkindlist)
- [GetSourceFilterList](#getsourcefilterlist)
- [GetSourceFilterDefaultSettings](#getsourcefilterdefaultsettings)
Expand All @@ -2758,7 +2758,7 @@ communication is desired.
- [SetSourceFilterIndex](#setsourcefilterindex)
- [SetSourceFilterSettings](#setsourcefiltersettings)
- [SetSourceFilterEnabled](#setsourcefilterenabled)
- [Scene Items Requests](#scene-items-1-requests)
- [Scene Items Requests](#scene-items-requests)
- [GetSceneItemList](#getsceneitemlist)
- [GetGroupSceneItemList](#getgroupsceneitemlist)
- [GetSceneItemId](#getsceneitemid)
Expand All @@ -2776,7 +2776,7 @@ communication is desired.
- [SetSceneItemIndex](#setsceneitemindex)
- [GetSceneItemBlendMode](#getsceneitemblendmode)
- [SetSceneItemBlendMode](#setsceneitemblendmode)
- [Outputs Requests](#outputs-1-requests)
- [Outputs Requests](#outputs-requests)
- [GetVirtualCamStatus](#getvirtualcamstatus)
- [ToggleVirtualCam](#togglevirtualcam)
- [StartVirtualCam](#startvirtualcam)
Expand Down Expand Up @@ -2810,12 +2810,12 @@ communication is desired.
- [ResumeRecord](#resumerecord)
- [SplitRecordFile](#splitrecordfile)
- [CreateRecordChapter](#createrecordchapter)
- [Media Inputs Requests](#media-inputs-1-requests)
- [Media Inputs Requests](#media-inputs-requests)
- [GetMediaInputStatus](#getmediainputstatus)
- [SetMediaInputCursor](#setmediainputcursor)
- [OffsetMediaInputCursor](#offsetmediainputcursor)
- [TriggerMediaInputAction](#triggermediainputaction)
- [Ui Requests](#ui-1-requests)
- [Ui Requests](#ui-requests)
- [GetStudioModeEnabled](#getstudiomodeenabled)
- [SetStudioModeEnabled](#setstudiomodeenabled)
- [OpenInputPropertiesDialog](#openinputpropertiesdialog)
Expand Down
Loading
Loading