@mootup/moot-sdk 0.1.0-rc.0 → 0.2.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.
@@ -0,0 +1,10 @@
1
+ import type { MCPClientLike, Session } from './session.js';
2
+ import { MootupNotOrientedError } from './session.js';
3
+ export type { MCPClientLike, Session };
4
+ export { MootupNotOrientedError };
5
+ export interface ConnectMootupOptions {
6
+ baseUrl: string;
7
+ auth?: string;
8
+ }
9
+ export declare function connectMootup(client: MCPClientLike, opts: ConnectMootupOptions): Promise<Session>;
10
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACvC,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAkGD,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,OAAO,CAAC,CAqClB"}
@@ -0,0 +1,126 @@
1
+ // AH-g: connectMootup — thin SDK helper that calls the shipped convo
2
+ // `orientation` MCP tool via a caller-supplied MCP client, surfaces a
3
+ // typed, frozen Session. Session is memoized per-client (inv 10).
4
+ import { MootupNotOrientedError } from './session.js';
5
+ export { MootupNotOrientedError };
6
+ // Per-process, per-MCP-client session cache (inv 10).
7
+ const sessionCache = new WeakMap();
8
+ // Redaction list — error messages must never surface these (inv 11 / T-2).
9
+ const REDACTION_SUBSTRINGS = ['Bearer ', 'Authorization', 'api_key', 'token'];
10
+ function redactError(message) {
11
+ let out = message;
12
+ for (const needle of REDACTION_SUBSTRINGS) {
13
+ if (out.includes(needle)) {
14
+ out = out.replace(new RegExp(needle, 'gi'), '<redacted>');
15
+ }
16
+ }
17
+ return out;
18
+ }
19
+ function resolveAuth(explicit) {
20
+ if (explicit)
21
+ return explicit;
22
+ const env = globalThis.process?.env ?? {};
23
+ if (env.MOOTUP_OAUTH_TOKEN)
24
+ return env.MOOTUP_OAUTH_TOKEN;
25
+ if (env.MOOTUP_API_KEY)
26
+ return env.MOOTUP_API_KEY;
27
+ throw new MootupNotOrientedError('No auth supplied (kwarg > MOOTUP_OAUTH_TOKEN > MOOTUP_API_KEY > error)');
28
+ }
29
+ const JWT_SHAPE_RE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
30
+ function validateBaseUrlOrigin(baseUrl, token) {
31
+ // T-1: if token is JWT-shaped (heuristic OAuth detection), compare the
32
+ // token's second segment (claims) iss URL origin to baseUrl origin.
33
+ if (!JWT_SHAPE_RE.test(token))
34
+ return;
35
+ const segments = token.split('.');
36
+ let claims;
37
+ try {
38
+ // base64url-decode the claims segment.
39
+ const b64 = segments[1].replace(/-/g, '+').replace(/_/g, '/');
40
+ const pad = b64.length % 4 === 0 ? '' : '='.repeat(4 - (b64.length % 4));
41
+ const decoded = globalThis.atob
42
+ ? globalThis.atob(b64 + pad)
43
+ : Buffer.from(b64 + pad, 'base64').toString('binary');
44
+ claims = JSON.parse(decoded);
45
+ }
46
+ catch {
47
+ return;
48
+ }
49
+ if (!claims.iss)
50
+ return;
51
+ let issOrigin;
52
+ let baseOrigin;
53
+ try {
54
+ issOrigin = new URL(claims.iss).origin;
55
+ baseOrigin = new URL(baseUrl).origin;
56
+ }
57
+ catch {
58
+ throw new Error('base_url or OAuth issuer URL is malformed');
59
+ }
60
+ if (issOrigin !== baseOrigin) {
61
+ throw new Error(`base_url origin (${baseOrigin}) does not match OAuth issuer origin (${issOrigin})`);
62
+ }
63
+ }
64
+ function extractStructured(resp) {
65
+ if (resp.isError) {
66
+ throw new Error('orientation tool returned isError=true');
67
+ }
68
+ if (resp.structuredContent && typeof resp.structuredContent === 'object') {
69
+ return resp.structuredContent;
70
+ }
71
+ // Fallback: parse the JSON text block if FastMCP emitted only text.
72
+ const text = resp.content?.find((c) => c.type === 'text')?.text;
73
+ if (text) {
74
+ try {
75
+ return JSON.parse(text);
76
+ }
77
+ catch {
78
+ // fall through to error below
79
+ }
80
+ }
81
+ throw new Error('orientation response missing structuredContent (requires convo ≥AH-g)');
82
+ }
83
+ function makeToolsProxy() {
84
+ return new Proxy({}, {
85
+ get(_target, prop) {
86
+ throw new MootupNotOrientedError(`Typed tool accessors are not yet implemented. Tried to access 'session.tools.${String(prop)}'. Use the caller-supplied MCP client directly until typed accessors ship.`);
87
+ },
88
+ });
89
+ }
90
+ export async function connectMootup(client, opts) {
91
+ const cached = sessionCache.get(client);
92
+ if (cached)
93
+ return cached;
94
+ const token = resolveAuth(opts.auth);
95
+ try {
96
+ validateBaseUrlOrigin(opts.baseUrl, token);
97
+ }
98
+ catch (err) {
99
+ throw new Error(redactError(err.message));
100
+ }
101
+ let resp;
102
+ try {
103
+ resp = await client.callTool({ name: 'orientation', arguments: {} });
104
+ }
105
+ catch (err) {
106
+ throw new Error(redactError(`orientation call failed: ${err.message}`));
107
+ }
108
+ const structured = extractStructured(resp);
109
+ const identity = structured.identity;
110
+ if (!identity || typeof identity.actor_id !== 'string') {
111
+ throw new Error('orientation response missing identity.actor_id');
112
+ }
113
+ const focus = structured.focus_space;
114
+ const spaceId = focus && typeof focus.space_id === 'string' ? focus.space_id : null;
115
+ const contextRaw = structured.context;
116
+ const orientationSummary = typeof contextRaw === 'string' ? contextRaw : '';
117
+ const session = Object.freeze({
118
+ participantId: identity.actor_id,
119
+ spaceId,
120
+ orientationSummary,
121
+ tools: makeToolsProxy(),
122
+ });
123
+ sessionCache.set(client, session);
124
+ return session;
125
+ }
126
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  export * from './client.js';
2
2
  export { makeCookieJarMiddleware } from './cookies.js';
3
3
  export type { paths, components, operations, webhooks } from './generated/paths.js';
4
+ export { connectMootup } from './connect.js';
5
+ export type { ConnectMootupOptions, MCPClientLike, Session, } from './connect.js';
6
+ export { MootupNotOrientedError } from './session.js';
4
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EACV,oBAAoB,EACpB,aAAa,EACb,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './client.js';
2
2
  export { makeCookieJarMiddleware } from './cookies.js';
3
+ export { connectMootup } from './connect.js';
4
+ export { MootupNotOrientedError } from './session.js';
3
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAM7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface MCPClientLike {
2
+ callTool(req: {
3
+ name: string;
4
+ arguments?: Record<string, unknown>;
5
+ }): Promise<{
6
+ content?: Array<{
7
+ type: string;
8
+ text?: string;
9
+ }>;
10
+ structuredContent?: unknown;
11
+ isError?: boolean;
12
+ }>;
13
+ }
14
+ export declare class MootupNotOrientedError extends Error {
15
+ constructor(message?: string);
16
+ }
17
+ export interface Session {
18
+ readonly participantId: string;
19
+ readonly spaceId: string | null;
20
+ readonly orientationSummary: string;
21
+ readonly tools: Record<string, never>;
22
+ }
23
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,GAAG,OAAO,CAAC;QACV,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjD,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,qBAAa,sBAAuB,SAAQ,KAAK;gBAE7C,OAAO,SAA2D;CAKrE;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CACvC"}
@@ -0,0 +1,10 @@
1
+ // AH-g: Session types + error classes for connectMootup.
2
+ // Duck-typed MCPClientLike avoids any dependency on @modelcontextprotocol/sdk
3
+ // (inv 8 — no MCP SDK package in dependencies).
4
+ export class MootupNotOrientedError extends Error {
5
+ constructor(message = 'Session not yet resolved — await connectMootup() first') {
6
+ super(message);
7
+ this.name = 'MootupNotOrientedError';
8
+ }
9
+ }
10
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,8EAA8E;AAC9E,gDAAgD;AAahD,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YACE,OAAO,GAAG,wDAAwD;QAElE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF"}
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@mootup/moot-sdk",
3
- "version": "0.1.0-rc.0",
4
- "description": "Typed HTTP client for the convo (mootup) API, generated from OpenAPI 3.1.",
3
+ "version": "0.2.1",
4
+ "description": "Typed HTTP client AND MCP harness helpers (connectMootup) for the convo (mootup) API. Zero MCP SDK dependency — duck-typed client interface.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/mootup-io/moot-cli-js.git",
9
+ "directory": "packages/moot-sdk"
10
+ },
6
11
  "type": "module",
7
12
  "main": "./dist/index.js",
8
13
  "types": "./dist/index.d.ts",
@@ -19,11 +24,12 @@
19
24
  "README.md"
20
25
  ],
21
26
  "engines": {
22
- "node": ">=20.0.0"
27
+ "node": ">=18.0.0"
23
28
  },
24
29
  "scripts": {
25
30
  "generate": "openapi-typescript openapi.yaml -o src/generated/paths.ts",
26
31
  "sync:oas": "node scripts/sync-oas.mjs",
32
+ "sync:contract": "node scripts/sync-contract.mjs",
27
33
  "build": "npm run generate && tsc -p tsconfig.build.json",
28
34
  "test": "vitest run",
29
35
  "test:watch": "vitest",
package/src/connect.ts ADDED
@@ -0,0 +1,152 @@
1
+ // AH-g: connectMootup — thin SDK helper that calls the shipped convo
2
+ // `orientation` MCP tool via a caller-supplied MCP client, surfaces a
3
+ // typed, frozen Session. Session is memoized per-client (inv 10).
4
+
5
+ import type { MCPClientLike, Session } from './session.js';
6
+ import { MootupNotOrientedError } from './session.js';
7
+
8
+ export type { MCPClientLike, Session };
9
+ export { MootupNotOrientedError };
10
+
11
+ export interface ConnectMootupOptions {
12
+ baseUrl: string;
13
+ auth?: string;
14
+ }
15
+
16
+ // Per-process, per-MCP-client session cache (inv 10).
17
+ const sessionCache = new WeakMap<object, Session>();
18
+
19
+ // Redaction list — error messages must never surface these (inv 11 / T-2).
20
+ const REDACTION_SUBSTRINGS = ['Bearer ', 'Authorization', 'api_key', 'token'];
21
+
22
+ function redactError(message: string): string {
23
+ let out = message;
24
+ for (const needle of REDACTION_SUBSTRINGS) {
25
+ if (out.includes(needle)) {
26
+ out = out.replace(new RegExp(needle, 'gi'), '<redacted>');
27
+ }
28
+ }
29
+ return out;
30
+ }
31
+
32
+ function resolveAuth(explicit?: string): string {
33
+ if (explicit) return explicit;
34
+ const env = globalThis.process?.env ?? {};
35
+ if (env.MOOTUP_OAUTH_TOKEN) return env.MOOTUP_OAUTH_TOKEN;
36
+ if (env.MOOTUP_API_KEY) return env.MOOTUP_API_KEY;
37
+ throw new MootupNotOrientedError(
38
+ 'No auth supplied (kwarg > MOOTUP_OAUTH_TOKEN > MOOTUP_API_KEY > error)',
39
+ );
40
+ }
41
+
42
+ const JWT_SHAPE_RE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
43
+
44
+ function validateBaseUrlOrigin(baseUrl: string, token: string): void {
45
+ // T-1: if token is JWT-shaped (heuristic OAuth detection), compare the
46
+ // token's second segment (claims) iss URL origin to baseUrl origin.
47
+ if (!JWT_SHAPE_RE.test(token)) return;
48
+ const segments = token.split('.');
49
+ let claims: { iss?: string };
50
+ try {
51
+ // base64url-decode the claims segment.
52
+ const b64 = segments[1].replace(/-/g, '+').replace(/_/g, '/');
53
+ const pad = b64.length % 4 === 0 ? '' : '='.repeat(4 - (b64.length % 4));
54
+ const decoded = globalThis.atob
55
+ ? globalThis.atob(b64 + pad)
56
+ : Buffer.from(b64 + pad, 'base64').toString('binary');
57
+ claims = JSON.parse(decoded) as { iss?: string };
58
+ } catch {
59
+ return;
60
+ }
61
+ if (!claims.iss) return;
62
+ let issOrigin: string;
63
+ let baseOrigin: string;
64
+ try {
65
+ issOrigin = new URL(claims.iss).origin;
66
+ baseOrigin = new URL(baseUrl).origin;
67
+ } catch {
68
+ throw new Error('base_url or OAuth issuer URL is malformed');
69
+ }
70
+ if (issOrigin !== baseOrigin) {
71
+ throw new Error(
72
+ `base_url origin (${baseOrigin}) does not match OAuth issuer origin (${issOrigin})`,
73
+ );
74
+ }
75
+ }
76
+
77
+ function extractStructured(resp: {
78
+ content?: Array<{ type: string; text?: string }>;
79
+ structuredContent?: unknown;
80
+ isError?: boolean;
81
+ }): Record<string, unknown> {
82
+ if (resp.isError) {
83
+ throw new Error('orientation tool returned isError=true');
84
+ }
85
+ if (resp.structuredContent && typeof resp.structuredContent === 'object') {
86
+ return resp.structuredContent as Record<string, unknown>;
87
+ }
88
+ // Fallback: parse the JSON text block if FastMCP emitted only text.
89
+ const text = resp.content?.find((c) => c.type === 'text')?.text;
90
+ if (text) {
91
+ try {
92
+ return JSON.parse(text) as Record<string, unknown>;
93
+ } catch {
94
+ // fall through to error below
95
+ }
96
+ }
97
+ throw new Error(
98
+ 'orientation response missing structuredContent (requires convo ≥AH-g)',
99
+ );
100
+ }
101
+
102
+ function makeToolsProxy(): Record<string, never> {
103
+ return new Proxy({} as Record<string, never>, {
104
+ get(_target, prop): never {
105
+ throw new MootupNotOrientedError(
106
+ `Typed tool accessors are not yet implemented. Tried to access 'session.tools.${String(prop)}'. Use the caller-supplied MCP client directly until typed accessors ship.`,
107
+ );
108
+ },
109
+ }) as Record<string, never>;
110
+ }
111
+
112
+ export async function connectMootup(
113
+ client: MCPClientLike,
114
+ opts: ConnectMootupOptions,
115
+ ): Promise<Session> {
116
+ const cached = sessionCache.get(client as object);
117
+ if (cached) return cached;
118
+
119
+ const token = resolveAuth(opts.auth);
120
+ try {
121
+ validateBaseUrlOrigin(opts.baseUrl, token);
122
+ } catch (err) {
123
+ throw new Error(redactError((err as Error).message));
124
+ }
125
+
126
+ let resp;
127
+ try {
128
+ resp = await client.callTool({ name: 'orientation', arguments: {} });
129
+ } catch (err) {
130
+ throw new Error(redactError(`orientation call failed: ${(err as Error).message}`));
131
+ }
132
+
133
+ const structured = extractStructured(resp);
134
+ const identity = structured.identity as { actor_id?: unknown } | undefined;
135
+ if (!identity || typeof identity.actor_id !== 'string') {
136
+ throw new Error('orientation response missing identity.actor_id');
137
+ }
138
+ const focus = structured.focus_space as { space_id?: unknown } | null | undefined;
139
+ const spaceId =
140
+ focus && typeof focus.space_id === 'string' ? focus.space_id : null;
141
+ const contextRaw = structured.context;
142
+ const orientationSummary = typeof contextRaw === 'string' ? contextRaw : '';
143
+
144
+ const session: Session = Object.freeze({
145
+ participantId: identity.actor_id,
146
+ spaceId,
147
+ orientationSummary,
148
+ tools: makeToolsProxy(),
149
+ });
150
+ sessionCache.set(client as object, session);
151
+ return session;
152
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,10 @@
1
1
  export * from './client.js';
2
2
  export { makeCookieJarMiddleware } from './cookies.js';
3
3
  export type { paths, components, operations, webhooks } from './generated/paths.js';
4
+ export { connectMootup } from './connect.js';
5
+ export type {
6
+ ConnectMootupOptions,
7
+ MCPClientLike,
8
+ Session,
9
+ } from './connect.js';
10
+ export { MootupNotOrientedError } from './session.js';
package/src/session.ts ADDED
@@ -0,0 +1,30 @@
1
+ // AH-g: Session types + error classes for connectMootup.
2
+ // Duck-typed MCPClientLike avoids any dependency on @modelcontextprotocol/sdk
3
+ // (inv 8 — no MCP SDK package in dependencies).
4
+
5
+ export interface MCPClientLike {
6
+ callTool(req: {
7
+ name: string;
8
+ arguments?: Record<string, unknown>;
9
+ }): Promise<{
10
+ content?: Array<{ type: string; text?: string }>;
11
+ structuredContent?: unknown;
12
+ isError?: boolean;
13
+ }>;
14
+ }
15
+
16
+ export class MootupNotOrientedError extends Error {
17
+ constructor(
18
+ message = 'Session not yet resolved — await connectMootup() first',
19
+ ) {
20
+ super(message);
21
+ this.name = 'MootupNotOrientedError';
22
+ }
23
+ }
24
+
25
+ export interface Session {
26
+ readonly participantId: string;
27
+ readonly spaceId: string | null;
28
+ readonly orientationSummary: string;
29
+ readonly tools: Record<string, never>;
30
+ }