obsbot-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/capture/ffmpeg-args.d.ts +17 -0
- package/dist/capture/ffmpeg-args.js +48 -0
- package/dist/capture/ffmpeg-args.js.map +1 -0
- package/dist/capture/manager.d.ts +60 -0
- package/dist/capture/manager.js +159 -0
- package/dist/capture/manager.js.map +1 -0
- package/dist/codec/commands.d.ts +63 -0
- package/dist/codec/commands.js +185 -0
- package/dist/codec/commands.js.map +1 -0
- package/dist/codec/crc.d.ts +2 -0
- package/dist/codec/crc.js +11 -0
- package/dist/codec/crc.js.map +1 -0
- package/dist/codec/encoding.d.ts +7 -0
- package/dist/codec/encoding.js +8 -0
- package/dist/codec/encoding.js.map +1 -0
- package/dist/codec/frame.d.ts +20 -0
- package/dist/codec/frame.js +73 -0
- package/dist/codec/frame.js.map +1 -0
- package/dist/codec/opcodes.d.ts +32 -0
- package/dist/codec/opcodes.js +509 -0
- package/dist/codec/opcodes.js.map +1 -0
- package/dist/codec/types.d.ts +13 -0
- package/dist/codec/types.js +2 -0
- package/dist/codec/types.js.map +1 -0
- package/dist/device/manager.d.ts +9 -0
- package/dist/device/manager.js +20 -0
- package/dist/device/manager.js.map +1 -0
- package/dist/device/session.d.ts +22 -0
- package/dist/device/session.js +37 -0
- package/dist/device/session.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/framing.d.ts +31 -0
- package/dist/mcp/framing.js +45 -0
- package/dist/mcp/framing.js.map +1 -0
- package/dist/mcp/ready.d.ts +31 -0
- package/dist/mcp/ready.js +71 -0
- package/dist/mcp/ready.js.map +1 -0
- package/dist/mcp/render.d.ts +3 -0
- package/dist/mcp/render.js +14 -0
- package/dist/mcp/render.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +49 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.js +580 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/transport/helper-process.d.ts +39 -0
- package/dist/transport/helper-process.js +140 -0
- package/dist/transport/helper-process.js.map +1 -0
- package/dist/transport/transport.d.ts +62 -0
- package/dist/transport/transport.js +8 -0
- package/dist/transport/transport.js.map +1 -0
- package/dist/transport/windows.d.ts +34 -0
- package/dist/transport/windows.js +68 -0
- package/dist/transport/windows.js.map +1 -0
- package/native/prebuilt/win32-x64/obsbot-helper.exe +0 -0
- package/package.json +33 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DeviceInfo } from "../codec/types.js";
|
|
2
|
+
import { Snapshot, SnapshotOpts } from "./transport.js";
|
|
3
|
+
export declare class HelperProcess {
|
|
4
|
+
private commandOverride?;
|
|
5
|
+
private proc?;
|
|
6
|
+
private rl?;
|
|
7
|
+
private queue;
|
|
8
|
+
constructor(commandOverride?: string[] | undefined);
|
|
9
|
+
static resolveBinaryPath(platform?: string, arch?: string): string;
|
|
10
|
+
start(): Promise<void>;
|
|
11
|
+
private rpcRaw;
|
|
12
|
+
private rpc;
|
|
13
|
+
version(): Promise<string>;
|
|
14
|
+
enumerate(): Promise<DeviceInfo[]>;
|
|
15
|
+
open(path: string): Promise<number>;
|
|
16
|
+
xuSet(selector: number, data: Buffer): Promise<void>;
|
|
17
|
+
xuGet(selector: number, length: number): Promise<Buffer>;
|
|
18
|
+
zoomRange(): Promise<{
|
|
19
|
+
min: number;
|
|
20
|
+
max: number;
|
|
21
|
+
}>;
|
|
22
|
+
zoomSet(units: number): Promise<void>;
|
|
23
|
+
snapshot(opts: SnapshotOpts): Promise<Snapshot>;
|
|
24
|
+
camCtrlSet(property: number, value: number, flags: number): Promise<void>;
|
|
25
|
+
camCtrlRange(property: number): Promise<{
|
|
26
|
+
min: number;
|
|
27
|
+
max: number;
|
|
28
|
+
}>;
|
|
29
|
+
camCtrlGet(property: number): Promise<{
|
|
30
|
+
value: number;
|
|
31
|
+
flags: number;
|
|
32
|
+
}>;
|
|
33
|
+
procAmpSet(property: number, value: number, flags: number): Promise<void>;
|
|
34
|
+
procAmpRange(property: number): Promise<{
|
|
35
|
+
min: number;
|
|
36
|
+
max: number;
|
|
37
|
+
}>;
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { CameraBusyError } from "./transport.js";
|
|
6
|
+
const SUPPORTED = { win32: "obsbot-helper.exe" };
|
|
7
|
+
export class HelperProcess {
|
|
8
|
+
commandOverride;
|
|
9
|
+
proc;
|
|
10
|
+
rl;
|
|
11
|
+
queue = [];
|
|
12
|
+
constructor(commandOverride) {
|
|
13
|
+
this.commandOverride = commandOverride;
|
|
14
|
+
}
|
|
15
|
+
static resolveBinaryPath(platform = process.platform, arch = process.arch) {
|
|
16
|
+
const bin = SUPPORTED[platform];
|
|
17
|
+
if (!bin) {
|
|
18
|
+
throw new Error(`transport not yet implemented for ${platform}`);
|
|
19
|
+
}
|
|
20
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
21
|
+
return join(root, "native", "prebuilt", `${platform}-${arch}`, bin);
|
|
22
|
+
}
|
|
23
|
+
async start() {
|
|
24
|
+
const [cmd, ...args] = this.commandOverride ?? [
|
|
25
|
+
HelperProcess.resolveBinaryPath(),
|
|
26
|
+
];
|
|
27
|
+
this.proc = spawn(cmd, args, { stdio: ["pipe", "pipe", "inherit"] });
|
|
28
|
+
this.rl = createInterface({ input: this.proc.stdout });
|
|
29
|
+
this.rl.on("line", (line) => {
|
|
30
|
+
if (!line.trim())
|
|
31
|
+
return;
|
|
32
|
+
let parsed;
|
|
33
|
+
try {
|
|
34
|
+
parsed = JSON.parse(line);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Not JSON at all — likely a stray log/diagnostic line the helper
|
|
38
|
+
// wrote to stdout. Ignore it without shifting the queue so it
|
|
39
|
+
// can't desync request/response correlation.
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (typeof parsed !== "object" ||
|
|
43
|
+
parsed === null ||
|
|
44
|
+
typeof parsed.ok !== "boolean") {
|
|
45
|
+
// Valid JSON but not a recognizable RPC response (missing/invalid
|
|
46
|
+
// `ok` field). Treat as noise rather than desyncing the queue.
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const cb = this.queue.shift();
|
|
50
|
+
if (cb)
|
|
51
|
+
cb(parsed);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Send a request and resolve with the raw response, whatever `ok` is.
|
|
55
|
+
rpcRaw(req) {
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
this.queue.push(resolve);
|
|
58
|
+
this.proc.stdin.write(JSON.stringify(req) + "\n");
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async rpc(req) {
|
|
62
|
+
const resp = await this.rpcRaw(req);
|
|
63
|
+
if (!resp.ok)
|
|
64
|
+
throw new Error(resp.error ?? "helper error (no message)");
|
|
65
|
+
return resp;
|
|
66
|
+
}
|
|
67
|
+
async version() {
|
|
68
|
+
const resp = await this.rpc({ op: "version" });
|
|
69
|
+
return resp.version;
|
|
70
|
+
}
|
|
71
|
+
async enumerate() {
|
|
72
|
+
const resp = await this.rpc({ op: "enumerate" });
|
|
73
|
+
return resp.devices;
|
|
74
|
+
}
|
|
75
|
+
async open(path) {
|
|
76
|
+
const resp = await this.rpc({ op: "open", path });
|
|
77
|
+
return resp.xuNode;
|
|
78
|
+
}
|
|
79
|
+
async xuSet(selector, data) {
|
|
80
|
+
await this.rpc({ op: "xu_set", selector, hex: data.toString("hex") });
|
|
81
|
+
}
|
|
82
|
+
async xuGet(selector, length) {
|
|
83
|
+
const resp = await this.rpc({ op: "xu_get", selector, length });
|
|
84
|
+
return Buffer.from(resp.hex, "hex");
|
|
85
|
+
}
|
|
86
|
+
async zoomRange() {
|
|
87
|
+
const resp = await this.rpc({ op: "zoom_range" });
|
|
88
|
+
return { min: resp.min, max: resp.max };
|
|
89
|
+
}
|
|
90
|
+
async zoomSet(units) {
|
|
91
|
+
await this.rpc({ op: "zoom_set", units });
|
|
92
|
+
}
|
|
93
|
+
async snapshot(opts) {
|
|
94
|
+
const req = { op: "snapshot" };
|
|
95
|
+
if (opts.path !== undefined)
|
|
96
|
+
req.path = opts.path;
|
|
97
|
+
if (opts.maxDim !== undefined)
|
|
98
|
+
req.maxDim = opts.maxDim;
|
|
99
|
+
if (opts.quality !== undefined)
|
|
100
|
+
req.quality = opts.quality;
|
|
101
|
+
if (opts.settleMs !== undefined)
|
|
102
|
+
req.settleMs = opts.settleMs;
|
|
103
|
+
const resp = await this.rpcRaw(req);
|
|
104
|
+
if (!resp.ok) {
|
|
105
|
+
if (resp.busy)
|
|
106
|
+
throw new CameraBusyError(resp.error ?? undefined);
|
|
107
|
+
throw new Error(resp.error ?? "snapshot failed");
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
mime: resp.mime,
|
|
111
|
+
width: resp.width,
|
|
112
|
+
height: resp.height,
|
|
113
|
+
base64: resp.base64,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async camCtrlSet(property, value, flags) {
|
|
117
|
+
await this.rpc({ op: "camctrl_set", property, value, flags });
|
|
118
|
+
}
|
|
119
|
+
async camCtrlRange(property) {
|
|
120
|
+
const resp = await this.rpc({ op: "camctrl_range", property });
|
|
121
|
+
return { min: resp.min, max: resp.max };
|
|
122
|
+
}
|
|
123
|
+
async camCtrlGet(property) {
|
|
124
|
+
const resp = await this.rpc({ op: "camctrl_get", property });
|
|
125
|
+
return { value: resp.value, flags: resp.flags };
|
|
126
|
+
}
|
|
127
|
+
async procAmpSet(property, value, flags) {
|
|
128
|
+
await this.rpc({ op: "procamp_set", property, value, flags });
|
|
129
|
+
}
|
|
130
|
+
async procAmpRange(property) {
|
|
131
|
+
const resp = await this.rpc({ op: "procamp_range", property });
|
|
132
|
+
return { min: resp.min, max: resp.max };
|
|
133
|
+
}
|
|
134
|
+
async close() {
|
|
135
|
+
this.rl?.close();
|
|
136
|
+
this.proc?.stdin.end();
|
|
137
|
+
this.proc?.kill();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=helper-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper-process.js","sourceRoot":"","sources":["../../src/transport/helper-process.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface Snapshot {
|
|
2
|
+
mime: string;
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
base64: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SnapshotOpts {
|
|
8
|
+
path?: string;
|
|
9
|
+
maxDim?: number;
|
|
10
|
+
quality?: number;
|
|
11
|
+
settleMs?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Thrown when a snapshot fails because another app holds the capture pin. */
|
|
14
|
+
export declare class CameraBusyError extends Error {
|
|
15
|
+
constructor(message?: string);
|
|
16
|
+
}
|
|
17
|
+
export interface ObsbotTransport {
|
|
18
|
+
sendVendor(frame: Buffer): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Send a vendor request frame (SET_CUR on the vendor selector) and read the
|
|
21
|
+
* reply frame back (GET_CUR on the response selector). Returns the raw reply
|
|
22
|
+
* bytes; callers parse with parseFrame.
|
|
23
|
+
*/
|
|
24
|
+
recvVendor(frame: Buffer, length?: number): Promise<Buffer>;
|
|
25
|
+
/**
|
|
26
|
+
* Read the camera's flat status block via GET_CUR on the status selector
|
|
27
|
+
* (no request frame is sent). Returns the raw block; callers use decodeStatus.
|
|
28
|
+
*/
|
|
29
|
+
recvStatus(length?: number): Promise<Buffer>;
|
|
30
|
+
/** Send a raw payload to an arbitrary UVC extension-unit selector (e.g. FOV/HDR on selector 6). */
|
|
31
|
+
xuRaw(selector: number, data: Buffer): Promise<void>;
|
|
32
|
+
/** RE/diagnostics: GET_CUR read `length` bytes from an arbitrary XU selector (no request frame sent). */
|
|
33
|
+
xuGetRaw(selector: number, length: number): Promise<Buffer>;
|
|
34
|
+
zoomRange(): Promise<{
|
|
35
|
+
min: number;
|
|
36
|
+
max: number;
|
|
37
|
+
}>;
|
|
38
|
+
zoomSet(units: number): Promise<void>;
|
|
39
|
+
/** Grab one still frame (grab-and-release). Throws CameraBusyError if the pin is busy. */
|
|
40
|
+
snapshot(opts: SnapshotOpts): Promise<Snapshot>;
|
|
41
|
+
/** IAMCameraControl::Set(property, value, flags) — used for focus. */
|
|
42
|
+
camCtrlSet(property: number, value: number, flags: number): Promise<void>;
|
|
43
|
+
/** IAMCameraControl::GetRange(property) → device-unit min/max. */
|
|
44
|
+
camCtrlRange(property: number): Promise<{
|
|
45
|
+
min: number;
|
|
46
|
+
max: number;
|
|
47
|
+
}>;
|
|
48
|
+
/** Read the current value + flags of an IAMCameraControl property (GET_CUR). */
|
|
49
|
+
camCtrlGet(property: number): Promise<{
|
|
50
|
+
value: number;
|
|
51
|
+
flags: number;
|
|
52
|
+
}>;
|
|
53
|
+
/** IAMVideoProcAmp::Set(property, value, flags) — used for white balance. */
|
|
54
|
+
procAmpSet(property: number, value: number, flags: number): Promise<void>;
|
|
55
|
+
/** IAMVideoProcAmp::GetRange(property) → device-unit min/max. */
|
|
56
|
+
procAmpRange(property: number): Promise<{
|
|
57
|
+
min: number;
|
|
58
|
+
max: number;
|
|
59
|
+
}>;
|
|
60
|
+
nextSeq(): number;
|
|
61
|
+
close(): Promise<void>;
|
|
62
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Thrown when a snapshot fails because another app holds the capture pin. */
|
|
2
|
+
export class CameraBusyError extends Error {
|
|
3
|
+
constructor(message = "camera in use by another application") {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "CameraBusyError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAcA,8EAA8E;AAC9E,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAO,GAAG,sCAAsC;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HelperProcess } from "./helper-process.js";
|
|
2
|
+
import { ObsbotTransport, Snapshot, SnapshotOpts } from "./transport.js";
|
|
3
|
+
export declare class WindowsTransport implements ObsbotTransport {
|
|
4
|
+
private helper;
|
|
5
|
+
private seq;
|
|
6
|
+
constructor(helper: HelperProcess);
|
|
7
|
+
sendVendor(frame: Buffer): Promise<void>;
|
|
8
|
+
recvVendor(frame: Buffer, length?: number): Promise<Buffer>;
|
|
9
|
+
recvStatus(length?: number): Promise<Buffer>;
|
|
10
|
+
xuRaw(selector: number, data: Buffer): Promise<void>;
|
|
11
|
+
xuGetRaw(selector: number, length: number): Promise<Buffer>;
|
|
12
|
+
zoomRange(): Promise<{
|
|
13
|
+
min: number;
|
|
14
|
+
max: number;
|
|
15
|
+
}>;
|
|
16
|
+
zoomSet(units: number): Promise<void>;
|
|
17
|
+
snapshot(opts: SnapshotOpts): Promise<Snapshot>;
|
|
18
|
+
camCtrlSet(property: number, value: number, flags: number): Promise<void>;
|
|
19
|
+
camCtrlRange(property: number): Promise<{
|
|
20
|
+
min: number;
|
|
21
|
+
max: number;
|
|
22
|
+
}>;
|
|
23
|
+
camCtrlGet(property: number): Promise<{
|
|
24
|
+
value: number;
|
|
25
|
+
flags: number;
|
|
26
|
+
}>;
|
|
27
|
+
procAmpSet(property: number, value: number, flags: number): Promise<void>;
|
|
28
|
+
procAmpRange(property: number): Promise<{
|
|
29
|
+
min: number;
|
|
30
|
+
max: number;
|
|
31
|
+
}>;
|
|
32
|
+
nextSeq(): number;
|
|
33
|
+
close(): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const VENDOR_XU_SELECTOR = 0x02;
|
|
2
|
+
// Intended read-back selector for the (future) per-command V3-frame Get path
|
|
3
|
+
// used by recvVendor. NOT YET verified on hardware — the per-command reply path
|
|
4
|
+
// is unproven scaffolding; Slice 1 shipped the status-block read instead. A
|
|
5
|
+
// separate constant from VENDOR_XU_SELECTOR because it may need to diverge once
|
|
6
|
+
// that path is validated. Change here only.
|
|
7
|
+
const RESPONSE_SELECTOR = 0x02;
|
|
8
|
+
const DEFAULT_REPLY_LEN = 60;
|
|
9
|
+
// The camera exposes a flat status block on this XU selector; GET_CUR reads it
|
|
10
|
+
// whole (see decodeStatus). Confirmed on the physical Tiny 2 (2026-07-13):
|
|
11
|
+
// awake (byte 0x02) and hdr (byte 0x06) round-trip against set_run_status/hdr.
|
|
12
|
+
const STATUS_SELECTOR = 0x06;
|
|
13
|
+
const STATUS_BLOCK_LEN = 60;
|
|
14
|
+
export class WindowsTransport {
|
|
15
|
+
helper;
|
|
16
|
+
seq = 0;
|
|
17
|
+
constructor(helper) {
|
|
18
|
+
this.helper = helper;
|
|
19
|
+
}
|
|
20
|
+
async sendVendor(frame) {
|
|
21
|
+
await this.helper.xuSet(VENDOR_XU_SELECTOR, frame);
|
|
22
|
+
}
|
|
23
|
+
async recvVendor(frame, length = DEFAULT_REPLY_LEN) {
|
|
24
|
+
await this.helper.xuSet(VENDOR_XU_SELECTOR, frame);
|
|
25
|
+
return this.helper.xuGet(RESPONSE_SELECTOR, length);
|
|
26
|
+
}
|
|
27
|
+
async recvStatus(length = STATUS_BLOCK_LEN) {
|
|
28
|
+
return this.helper.xuGet(STATUS_SELECTOR, length);
|
|
29
|
+
}
|
|
30
|
+
async xuRaw(selector, data) {
|
|
31
|
+
await this.helper.xuSet(selector, data);
|
|
32
|
+
}
|
|
33
|
+
async xuGetRaw(selector, length) {
|
|
34
|
+
return this.helper.xuGet(selector, length);
|
|
35
|
+
}
|
|
36
|
+
async zoomRange() {
|
|
37
|
+
return this.helper.zoomRange();
|
|
38
|
+
}
|
|
39
|
+
async zoomSet(units) {
|
|
40
|
+
await this.helper.zoomSet(units);
|
|
41
|
+
}
|
|
42
|
+
async snapshot(opts) {
|
|
43
|
+
return this.helper.snapshot(opts);
|
|
44
|
+
}
|
|
45
|
+
async camCtrlSet(property, value, flags) {
|
|
46
|
+
await this.helper.camCtrlSet(property, value, flags);
|
|
47
|
+
}
|
|
48
|
+
async camCtrlRange(property) {
|
|
49
|
+
return this.helper.camCtrlRange(property);
|
|
50
|
+
}
|
|
51
|
+
async camCtrlGet(property) {
|
|
52
|
+
return this.helper.camCtrlGet(property);
|
|
53
|
+
}
|
|
54
|
+
async procAmpSet(property, value, flags) {
|
|
55
|
+
await this.helper.procAmpSet(property, value, flags);
|
|
56
|
+
}
|
|
57
|
+
async procAmpRange(property) {
|
|
58
|
+
return this.helper.procAmpRange(property);
|
|
59
|
+
}
|
|
60
|
+
nextSeq() {
|
|
61
|
+
this.seq = this.seq >= 0xffff ? 1 : this.seq + 1;
|
|
62
|
+
return this.seq;
|
|
63
|
+
}
|
|
64
|
+
async close() {
|
|
65
|
+
await this.helper.close();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=windows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows.js","sourceRoot":"","sources":["../../src/transport/windows.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obsbot-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cross-platform MCP server for OBSBOT Tiny 2 (UVC) camera control",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Michael Jordan",
|
|
7
|
+
"repository": { "type": "git", "url": "git+https://github.com/lxman/obsbot-mcp.git" },
|
|
8
|
+
"homepage": "https://github.com/lxman/obsbot-mcp#readme",
|
|
9
|
+
"bugs": { "url": "https://github.com/lxman/obsbot-mcp/issues" },
|
|
10
|
+
"keywords": ["mcp", "model-context-protocol", "obsbot", "webcam", "uvc", "ptz", "camera"],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": { "obsbot-mcp": "dist/index.js" },
|
|
13
|
+
"files": ["dist/", "native/prebuilt/"],
|
|
14
|
+
"engines": { "node": ">=18" },
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
22
|
+
"zod": "^3.23.0",
|
|
23
|
+
"zod-to-json-schema": "^3.23.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "^5.5.0",
|
|
27
|
+
"vitest": "^2.0.0",
|
|
28
|
+
"@types/node": "^20.0.0"
|
|
29
|
+
},
|
|
30
|
+
"allowScripts": {
|
|
31
|
+
"esbuild@0.21.5": true
|
|
32
|
+
}
|
|
33
|
+
}
|