@ours.network/mcp 0.17.0 → 0.18.0-nightly.1
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/README.md +66 -115
- package/dist/application-identities.js +2 -0
- package/dist/cli.js +4 -127
- package/dist/connector.js +74 -0
- package/dist/contacts.js +0 -1
- package/dist/push.js +1 -0
- package/package.json +6 -11
- package/dist/files.js +0 -2
- package/dist/inbox.js +0 -2
- package/dist/index.js +0 -106
- package/dist/mufl_code/8ED6356B41CC689C75DA3302AD7856058131661A6ABBAC961E71BA838D8C97A3.muflo +0 -0
- package/dist/process-state.js +0 -2
- package/dist/sse-keepalive.js +0 -4
- package/dist/startup-progress.js +0 -4
- package/dist/transcribe.js +0 -2
- package/dist/voice-setup.js +0 -28
package/README.md
CHANGED
|
@@ -1,123 +1,74 @@
|
|
|
1
1
|
# @ours.network/mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
identity owned by exactly one session lease: on close/session end, contacts get
|
|
22
|
-
one best-effort remove-me notice, then ALL local state is deleted (stale ones —
|
|
23
|
-
owner process dead — are swept automatically; permanent identities never are)
|
|
24
|
-
- `send_message` — end-to-end encrypted; optional `reply_to_wire_id` (+ `reply_to_sentence`) to reply to a specific message
|
|
25
|
-
- `get_messages` — return unread messages (bodies, each with its `wire_id` + any `reply_to`) + mark read; delivered exactly once
|
|
26
|
-
- `mark_processed` / `defer_messages` — remove handled messages, or re-queue read ones for another session
|
|
27
|
-
- `list_incoming_messages` — full inbox with ids + status (read-only)
|
|
28
|
-
- `list_incoming_files` — byte-free structured preauthorization metadata, including authenticated sender CID (`from.id`), separate display name, stable IDs, size/date/status, filename and MIME
|
|
29
|
-
- `get_files({ wire_ids? })` — atomically retrieve only approved unread wire IDs; omitting `wire_ids` preserves legacy all-unread retrieval. Results include safe local paths, actual size/hash, provenance/status, and structured voice transcription outcomes
|
|
30
|
-
|
|
31
|
-
File wake events are content-free but correlation-complete: authenticated `sender_id`,
|
|
32
|
-
`file_id`, `wire_id`, display name, filename, MIME, byte count, and received date. A caller
|
|
33
|
-
must authorize against `sender_id`, never the untrusted display label. Selected IDs are
|
|
34
|
-
unique 64-hex wire IDs (maximum 32); malformed, duplicate, unknown, or stale selections
|
|
35
|
-
fail atomically without retrieving or writing any file.
|
|
36
|
-
|
|
37
|
-
## Configuration
|
|
38
|
-
|
|
39
|
-
| Env var | Default | Meaning |
|
|
40
|
-
|---------|---------|---------|
|
|
41
|
-
| `OURS_STATE_DIR` | `~/.ours` | Node identity + serialized state. Distinct per node. |
|
|
42
|
-
| `OURS_BROKER_URL` | `wss://broker1.ours.network` | The ADAPT broker to connect through. Set to `ws://localhost:9000` for a local broker. |
|
|
43
|
-
|
|
44
|
-
### Voice-message transcription
|
|
45
|
-
|
|
46
|
-
Voice transcription is off until a provider and key (plus provider-required fields) are
|
|
47
|
-
configured. Check readiness without exposing credentials:
|
|
3
|
+
The agent-facing MCP adapter for the shared ours daemon.
|
|
4
|
+
|
|
5
|
+
`ours-mcp` does not contain, start, configure, or install a daemon. Install
|
|
6
|
+
`@ours.network/cli@1.0.1`, configure it with `ours config setup`, and start the
|
|
7
|
+
single shared service with `ours daemon start` (or `ours daemon install-service`).
|
|
8
|
+
|
|
9
|
+
## MCP configuration
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"ours": {
|
|
15
|
+
"command": "ours-mcp",
|
|
16
|
+
"args": ["proxy"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
48
21
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
22
|
+
`proxy` attaches through `@ours.network/sdk@2.0.1`. It uses the SDK's coherent
|
|
23
|
+
daemon selection (`OURS_CONFIG`, or matching `OURS_PORT` and `OURS_STATE_DIR`)
|
|
24
|
+
and verifies `/state-dir` before credentials are sent. An unavailable daemon is
|
|
25
|
+
reported with install/start guidance; it is never started inside the MCP process.
|
|
26
|
+
|
|
27
|
+
Legacy daemon variables such as `OURS_AUTOSTART`, `OURS_TRANSPORT`, and
|
|
28
|
+
`OURS_UNIT_DIR` are rejected. Named `--application` selections are also rejected;
|
|
29
|
+
use the SDK daemon selection variables instead.
|
|
30
|
+
|
|
31
|
+
## Application identity list
|
|
32
|
+
|
|
33
|
+
The daemon hosts all identities. ours-mcp keeps only the identities adopted by
|
|
34
|
+
this application and filters global listings through that set. Fresh installs
|
|
35
|
+
start empty. Creating an identity or successfully calling `choose_identity`
|
|
36
|
+
adopts it; closing or removing one deletes it from the application list.
|
|
37
|
+
|
|
38
|
+
The file defaults to `~/.ours-mcp/config.json` and can be overridden for tests
|
|
39
|
+
with `OURS_MCP_CONFIG`. Its versioned shape is:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"version": 1,
|
|
44
|
+
"daemons": {
|
|
45
|
+
"/absolute/daemon/state-dir": {
|
|
46
|
+
"identities": ["BuildBot"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Telegram fallback preserves its original OGG/Opus bytes and `.ogg` filename and advertises
|
|
72
|
-
`audio/ogg; x-ours-kind=voice-message`; the connector's v2 message envelope correlates the
|
|
73
|
-
separate file using `attachment.wire_id`.
|
|
74
|
-
|
|
75
|
-
## Daemon lifecycle
|
|
76
|
-
|
|
77
|
-
This package is the **single owner of the daemon lifecycle**. `ours-mcp start`
|
|
78
|
-
runs one long-lived HTTP daemon per host (default port 3050) that hosts every
|
|
79
|
-
identity's packet, the broker socket, and file locks — a shared singleton that
|
|
80
|
-
cannot be run per session. Each session instead runs a thin `ours-mcp proxy`
|
|
81
|
-
(stdio ⇄ the daemon's HTTP endpoint), which auto-starts the daemon if it is down.
|
|
82
|
-
Platform plugins ship only the proxy invocation; they never own or restart the
|
|
83
|
-
daemon.
|
|
84
|
-
|
|
85
|
-
`ours-mcp start` and `ours-mcp restart` do not treat an open socket as readiness.
|
|
86
|
-
They wait for an authenticated response from the daemon's normal control
|
|
87
|
-
surface (`/identities`) after the protocol runtime, contact-book registrar,
|
|
88
|
-
persisted identities, and boot reconciliation are complete. In owner mode the
|
|
89
|
-
CLI dynamically discovers the mode-`0600` token minted by the daemon before it
|
|
90
|
-
declares readiness; shared and open modes preserve their configured auth
|
|
91
|
-
semantics. During that wait the daemon publishes a mode-`0600`
|
|
92
|
-
`startup-progress.json` in its state directory. The structured record contains
|
|
93
|
-
only a phase, timestamps, process/boot identifiers, and identity counts — never
|
|
94
|
-
identity names, container IDs, keys, packet contents, or state paths.
|
|
95
|
-
|
|
96
|
-
Interactive terminals update one progress line; redirected/noninteractive runs
|
|
97
|
-
emit concise stable lines such as `startup: Restoring identities 3/12`. A
|
|
98
|
-
heartbeat distinguishes active work from a frozen process: 30 seconds without
|
|
99
|
-
an update is a failure, and an absolute three-minute bound prevents an
|
|
100
|
-
event-loop-active stall from waiting forever. Immediate daemon failure remains
|
|
101
|
-
nonzero. The same daemon bootstrap/reporting path is used by foreground
|
|
102
|
-
`serve`, Linux systemd, macOS launchd, and either native or WASM-backed ADAPT
|
|
103
|
-
runtimes; service managers keep their existing lifecycle behavior.
|
|
104
|
-
|
|
105
|
-
On connect, the proxy runs a compatibility handshake against the daemon's
|
|
106
|
-
`/state-dir` report (`{ version, compat }`). `compat` is the wire-contract
|
|
107
|
-
version (`src/protocol.ts`) — distinct from the package version, bumped only on
|
|
108
|
-
breaking proxy↔daemon changes. Matching `compat` proceeds; a differing package
|
|
109
|
-
version warns (stderr); an incompatible `compat` refuses with guidance to run
|
|
110
|
-
`ours-mcp stop`. The proxy never kills the shared daemon itself, since it may
|
|
111
|
-
be hosting other sessions' identities.
|
|
112
|
-
|
|
113
|
-
## Build
|
|
52
|
+
This list is application bookkeeping, not authorization. `choose_identity`
|
|
53
|
+
remains able to select any daemon identity and adopts it on success. Vanished
|
|
54
|
+
names remain recorded but are not rendered; idempotent close/remove cleans them.
|
|
55
|
+
|
|
56
|
+
## Compatibility CLI
|
|
57
|
+
|
|
58
|
+
Former ours-mcp lifecycle entry points remain compatibility aliases that
|
|
59
|
+
delegate argv, stdio, and exit status to `ours daemon`. The `ours` executable
|
|
60
|
+
must be on `PATH`, or its exact path may be provided through `OURS_CLI`. No
|
|
61
|
+
command falls back to an embedded daemon.
|
|
62
|
+
|
|
63
|
+
`ours-mcp watch [identity]` streams inbound JSON Lines. With no identity argument,
|
|
64
|
+
only live identities in the selected daemon's ours-mcp application list are
|
|
65
|
+
watched.
|
|
66
|
+
|
|
67
|
+
## Development
|
|
114
68
|
|
|
115
69
|
```sh
|
|
116
|
-
npm
|
|
117
|
-
npm run build
|
|
118
|
-
npm run typecheck
|
|
119
|
-
npm
|
|
70
|
+
npm install
|
|
71
|
+
npm run build --workspace @ours.network/mcp
|
|
72
|
+
npm run typecheck --workspace @ours.network/mcp
|
|
73
|
+
npm test --workspace @ours.network/mcp
|
|
120
74
|
```
|
|
121
|
-
|
|
122
|
-
See the [repo README](https://github.com/adapt-toolkit/ours-mcp#readme) for install
|
|
123
|
-
and quickstart.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{randomUUID as g}from"node:crypto";import{chmod as l,mkdir as u,readFile as w,rename as h,unlink as I,writeFile as v}from"node:fs/promises";import{homedir as E}from"node:os";import{dirname as A,resolve as s}from"node:path";var m=1,N="OURS_MCP_CONFIG",P=()=>({version:1,daemons:{}});function S(e=process.env){let t=(e[N]??"").trim();return s(t||s(E(),".ours-mcp","config.json"))}function C(e,t){let i;try{i=JSON.parse(e)}catch(o){throw new Error(`Invalid ours-mcp application identity config at ${t}: ${String(o)}`)}if(!i||typeof i!="object"||Array.isArray(i))throw new Error(`Invalid ours-mcp application identity config at ${t}: expected an object.`);let n=i;if(n.version!==m)throw new Error(`Unsupported ours-mcp application identity config version at ${t}: expected ${m}, found ${String(n.version)}.`);if(!n.daemons||typeof n.daemons!="object"||Array.isArray(n.daemons))throw new Error(`Invalid ours-mcp application identity config at ${t}: "daemons" must be an object.`);let r={};for(let[o,a]of Object.entries(n.daemons)){if(!a||typeof a!="object"||Array.isArray(a))throw new Error(`Invalid ours-mcp application identity config at ${t}: daemon ${JSON.stringify(o)} must be an object.`);let c=a;if(!Array.isArray(c.identities)||c.identities.some(p=>typeof p!="string"||p.length===0))throw new Error(`Invalid ours-mcp application identity config at ${t}: daemon ${JSON.stringify(o)} has an invalid identity list.`);r[s(o)]={identities:[...new Set(c.identities)].sort()}}return{version:1,daemons:r}}async function d(e){try{return C(await w(e,"utf8"),e)}catch(t){if(t.code==="ENOENT")return P();throw t}}async function f(e,t){let i=A(e);await u(i,{recursive:!0,mode:448});let n=`${e}.${process.pid}.${g()}.tmp`;try{await v(n,`${JSON.stringify(t,null,2)}
|
|
2
|
+
`,{mode:384,flag:"wx"}),await h(n,e),await l(e,384)}catch(r){try{await I(n)}catch{}throw r}}var y=class{stateDir;path;constructor(t,i={}){this.stateDir=s(t),this.path=s(i.path??S(i.env))}async list(){return[...(await d(this.path)).daemons[this.stateDir]?.identities??[]]}async has(t){return(await this.list()).includes(t)}async add(t){if(!t)throw new Error("Cannot add an empty identity name to ours-mcp.");let i=await d(this.path),n=i.daemons[this.stateDir]?.identities??[];n.includes(t)||(i.daemons[this.stateDir]={identities:[...n,t].sort()},await f(this.path,i))}async remove(t){let i=await d(this.path),n=i.daemons[this.stateDir];if(!n?.identities.includes(t))return;let r=n.identities.filter(o=>o!==t);r.length===0?delete i.daemons[this.stateDir]:i.daemons[this.stateDir]={identities:r},await f(this.path,i)}};async function b(e,t){let i=new Set(await e.list());return t.filter(n=>i.has(n.name))}export{N as APPLICATION_IDENTITIES_ENV,m as APPLICATION_IDENTITIES_VERSION,y as ApplicationIdentityStore,S as applicationIdentityConfigPath,b as filterApplicationIdentities};
|