@ours.network/mcp 1.0.1 → 1.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/README.md +12 -2
- package/dist/cli.js +1 -1
- package/dist/connector.js +25 -25
- package/dist/server.js +21 -21
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ single shared service with `ours daemon start` (or `ours daemon install-service`
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
`proxy` attaches through `@ours.network/sdk@3.
|
|
22
|
+
`proxy` attaches through `@ours.network/sdk@3.7.0`. It uses the SDK's coherent
|
|
23
23
|
daemon selection (`OURS_CONFIG`, or matching `OURS_PORT` and `OURS_STATE_DIR`)
|
|
24
24
|
and verifies `/state-dir` before credentials are sent. An unavailable daemon is
|
|
25
25
|
reported with install/start guidance; it is never started inside the MCP process.
|
|
@@ -79,10 +79,20 @@ that finds old packet state refuses startup without changing it. Operators may
|
|
|
79
79
|
back it up and must remove it themselves before starting clean; installers never
|
|
80
80
|
delete identity state implicitly.
|
|
81
81
|
|
|
82
|
-
The external-history integration requires the published `@ours.network/sdk@3.
|
|
82
|
+
The external-history and typed-command integration requires the published `@ours.network/sdk@3.7.0`
|
|
83
83
|
and `@ours.network/cli@2.2.0` artifacts. Both are pinned exactly so registry-only
|
|
84
84
|
installs use the validated contract; there is no compatibility fallback.
|
|
85
85
|
|
|
86
|
+
## Typed commands
|
|
87
|
+
|
|
88
|
+
`list_contact_commands` returns a contact's advertised command catalog as data.
|
|
89
|
+
`send_command` accepts arbitrary JSON-compatible arguments, delegates advertised-schema
|
|
90
|
+
validation and transport to the SDK, and returns the request wire ID. Later
|
|
91
|
+
`get_messages` calls expose `command_results` (correlated by `reply_to.wire_id`) and
|
|
92
|
+
`commands_handled` alongside ordinary unread messages. The connector never turns a
|
|
93
|
+
remote catalog into dynamically registered MCP tools and does not advertise commands
|
|
94
|
+
of its own.
|
|
95
|
+
|
|
86
96
|
## Development
|
|
87
97
|
|
|
88
98
|
```sh
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import{spawn as L}from"node:child_process";import{realpathSync as v}from"node:fs";import{fileURLToPath as j}from"node:url";import{attachOursClient as S,resolveDaemonConfig as J}from"@ours.network/sdk";import{randomUUID as O}from"node:crypto";import{chmod as C,mkdir as _,readFile as P,rename as A,unlink as D,writeFile as T}from"node:fs/promises";import{homedir as R}from"node:os";import{dirname as b,resolve as d}from"node:path";var g=1,$="OURS_MCP_CONFIG",x=()=>({version:1,daemons:{}});function U(t=process.env){let e=(t[$]??"").trim();return d(e||d(R(),".ours-mcp","config.json"))}function k(t,e){let i;try{i=JSON.parse(t)}catch(o){throw new Error(`Invalid ours-mcp application identity config at ${e}: ${String(o)}`)}if(!i||typeof i!="object"||Array.isArray(i))throw new Error(`Invalid ours-mcp application identity config at ${e}: expected an object.`);let n=i;if(n.version!==g)throw new Error(`Unsupported ours-mcp application identity config version at ${e}: expected ${g}, 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 ${e}: "daemons" must be an object.`);let s={};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 ${e}: daemon ${JSON.stringify(o)} must be an object.`);let c=a;if(!Array.isArray(c.identities)||c.identities.some(w=>typeof w!="string"||w.length===0))throw new Error(`Invalid ours-mcp application identity config at ${e}: daemon ${JSON.stringify(o)} has an invalid identity list.`);s[d(o)]={identities:[...new Set(c.identities)].sort()}}return{version:1,daemons:s}}async function m(t){try{return k(await P(t,"utf8"),t)}catch(e){if(e.code==="ENOENT")return x();throw e}}async function h(t,e){let i=b(t);await _(i,{recursive:!0,mode:448});let n=`${t}.${process.pid}.${O()}.tmp`;try{await T(n,`${JSON.stringify(e,null,2)}
|
|
3
|
-
`,{mode:384,flag:"wx"}),await A(n,t),await C(t,384)}catch(s){try{await D(n)}catch{}throw s}}var p=class{stateDir;path;constructor(e,i={}){this.stateDir=d(e),this.path=d(i.path??U(i.env))}async list(){return[...(await m(this.path)).daemons[this.stateDir]?.identities??[]]}async has(e){return(await this.list()).includes(e)}async add(e){if(!e)throw new Error("Cannot add an empty identity name to ours-mcp.");let i=await m(this.path),n=i.daemons[this.stateDir]?.identities??[];n.includes(e)||(i.daemons[this.stateDir]={identities:[...n,e].sort()},await h(this.path,i))}async remove(e){let i=await m(this.path),n=i.daemons[this.stateDir];if(!n?.identities.includes(e))return;let s=n.identities.filter(o=>o!==e);s.length===0?delete i.daemons[this.stateDir]:i.daemons[this.stateDir]={identities:s},await h(this.path,i)}};async function y(t,e){let i=new Set(await t.list());return e.filter(n=>i.has(n.name))}var l="1.0
|
|
3
|
+
`,{mode:384,flag:"wx"}),await A(n,t),await C(t,384)}catch(s){try{await D(n)}catch{}throw s}}var p=class{stateDir;path;constructor(e,i={}){this.stateDir=d(e),this.path=d(i.path??U(i.env))}async list(){return[...(await m(this.path)).daemons[this.stateDir]?.identities??[]]}async has(e){return(await this.list()).includes(e)}async add(e){if(!e)throw new Error("Cannot add an empty identity name to ours-mcp.");let i=await m(this.path),n=i.daemons[this.stateDir]?.identities??[];n.includes(e)||(i.daemons[this.stateDir]={identities:[...n,e].sort()},await h(this.path,i))}async remove(e){let i=await m(this.path),n=i.daemons[this.stateDir];if(!n?.identities.includes(e))return;let s=n.identities.filter(o=>o!==e);s.length===0?delete i.daemons[this.stateDir]:i.daemons[this.stateDir]={identities:s},await h(this.path,i)}};async function y(t,e){let i=new Set(await t.list());return e.filter(n=>i.has(n.name))}var l="1.1.0",r=t=>{process.stdout.write(`${t}
|
|
4
4
|
`)},N=t=>{process.stderr.write(`${t}
|
|
5
5
|
`)},I=t=>Number.isInteger(t)&&t>1,E=Number(process.env.OURS_CLIENT_PID),u=I(E)?E:I(process.ppid)?process.ppid:process.pid,f=(process.env.CLAUDE_CODE_SESSION_ID??"").trim()||`ours-mcp:${u}`,M=(process.env.OURS_BIND_IDENTITY??"").trim()||void 0,V=new Set(["start","stop","restart","serve","run","status","install-service","uninstall-service"]);function F(t){if(t.some(e=>e==="--application"||e.startsWith("--application=")))throw new Error("`--application` is no longer supported. ours-mcp connects to one coherently selected shared daemon; use OURS_CONFIG or a matching OURS_PORT + OURS_STATE_DIR selection.")}async function G(t){let i=(process.env.OURS_CLI??"").trim()||"ours";await new Promise((n,s)=>{let o=L(i,t,{stdio:"inherit",env:process.env});o.once("error",a=>{if(a.code==="ENOENT"){s(new Error(`Cannot find the ${JSON.stringify(i)} CLI. Install @ours.network/cli@2.2.0, put \`ours\` on PATH, or set OURS_CLI to its executable path.`));return}s(a)}),o.once("exit",(a,c)=>{if(c){process.kill(process.pid,c);return}process.exitCode=a??1,n()})})}async function W(){let t=new URL("./connector.js",import.meta.url),{runConnector:e}=await import(t.href);await e({leaseToken:f,clientPid:u,version:l,bindIdentity:M})}async function B(){await(await S({leaseToken:f,clientPid:u})).releaseLease()}function Y(t,e){return JSON.stringify({identity:t,...e})}async function q(t,e,i){for await(let n of t.watchNotifications(e,{kinds:["inbound"],signal:i}))r(Y(e,n))}async function H(t){if(t.length>1||t[0]?.startsWith("-"))throw new Error("Usage: ours-mcp watch [identity]");let e=J(),i=new p(e.expectStateDir);await i.list();let n=await S({leaseToken:f,clientPid:u}),s=t[0]?.trim(),o;s?o=[s]:o=(await y(i,await n.identities())).map(c=>c.name);let a=new AbortController;process.once("SIGINT",()=>a.abort()),process.once("SIGTERM",()=>a.abort()),N(`ours-mcp watch: ${o.length===0?"no application identities":`watching ${o.join(", ")}`}`),await Promise.all(o.map(c=>q(n,c,a.signal)))}function K(){r(`ours-mcp ${l} \u2014 MCP adapter for the shared ours daemon`),r(""),r("Usage: ours-mcp <command> [options]"),r(" proxy run the stdio MCP server (never starts a daemon)"),r(" session-end release this harness session and clean up its temporary identities"),r(" watch [identity] stream inbound JSON Lines; without a name, only ours-mcp identities"),r(" start|stop|restart|serve|status"),r(" install-service|uninstall-service"),r(" compatibility aliases for `ours daemon <command>`"),r(" version print the ours-mcp package version"),r(""),r("Daemon configuration and identity CLI operations moved to @ours.network/cli@2.2.0:"),r(" ours config setup"),r(" ours identity --help")}async function z(){let t=process.argv.slice(2);F(t);let e=t.shift()??"help";if(V.has(e)){await G(["daemon",e==="run"?"serve":e,...t]);return}switch(e){case"proxy":if(t.length)throw new Error("Usage: ours-mcp proxy");await W();return;case"session-end":if(t.length)throw new Error("Usage: ours-mcp session-end");await B();return;case"watch":await H(t);return;case"setup":throw new Error("ours-mcp no longer owns daemon configuration. Run `ours config setup` instead.");case"create-root":case"define-local-identity-file":case"voice-setup":case"voice-status":case"stt-setup":case"stt-status":throw new Error(`ours-mcp ${e} moved to the \`ours\` CLI. Run \`ours --help\` for the typed replacement.`);case"version":case"--version":case"-v":r(l);return;case"help":case"--help":case"-h":K();return;default:throw new Error(`Unknown command ${JSON.stringify(e)}. Run \`ours-mcp --help\`.`)}}function Q(){if(!process.argv[1])return!1;try{return v(process.argv[1])===v(j(import.meta.url))}catch{return!1}}var X=Q();X&&z().catch(t=>{N(`ours-mcp error: ${t instanceof Error?t.message:String(t)}`),process.exitCode=1});
|