ogment 0.2.2 → 0.2.4

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 CHANGED
@@ -18,9 +18,9 @@ Without a control plane, you're either giving every AI client raw API keys (inse
18
18
  - **Connect anything** — SaaS tools (Salesforce, Slack, Notion), internal APIs, databases, data warehouses
19
19
  - **Enterprise SSO** — Okta, Azure AD, Google Workspace — federated auth across your organization
20
20
  - **Integration marketplace** — 300+ pre-built connectors, community-contributed, security-scanned
21
+ - **Per-agent permissions** — control which servers each agent can access, with public and restricted servers
21
22
  - **Agent approval flows** — human-in-the-loop for sensitive operations, per-tool granular permissions
22
- - **Full audit trail** — every tool call logged, searchable, and exportable
23
- - **Works for agents and humans** — CLI for agents via exec, dashboard for humans, API for custom backends
23
+ - **Full audit trail** — every tool call logged per agent, searchable, and exportable
24
24
  - **Portable across AI clients** — Cursor, OpenClaw, Claude Desktop, ChatGPT, or your own agents — same config, same permissions
25
25
 
26
26
  ## How It Works
@@ -29,19 +29,19 @@ Without a control plane, you're either giving every AI client raw API keys (inse
29
29
  Any AI Client
30
30
  (OpenClaw, Cursor, Claude, ChatGPT, custom agents)
31
31
 
32
- │ ogment call <server> <tool> <args>
32
+ │ ogment --agentId <id> call <server> <tool> <args>
33
33
 
34
34
  ┌─────────────────────────────────────────┐
35
35
  │ Ogment Platform │
36
36
  │ │
37
37
  │ ✓ Authenticate (OAuth, SSO, tokens) │
38
+ │ ✓ Identify agent + enforce permissions │
38
39
  │ ✓ Portable workflows (MCP, Skills, │
39
40
  │ Claude Plugins) — author once, │
40
41
  │ deploy to any AI client │
41
- │ ✓ Enforce permissions per tool │
42
42
  │ ✓ Human approval for sensitive ops │
43
43
  │ ✓ Inject real credentials server-side │
44
- │ ✓ Log every call for audit
44
+ │ ✓ Log every call per agent for audit
45
45
  └──────────────┬──────────────────────────┘
46
46
 
47
47
  ┌──────────┼──────────┐
@@ -58,42 +58,49 @@ npm install -g ogment
58
58
 
59
59
  **`ogment login`** — Authenticate via browser OAuth. Auto-discovers your orgs and servers.
60
60
 
61
- **`ogment servers [path]`** — List all servers, or inspect a specific server's tools:
62
- ```
63
- ogment servers · ogment servers salesforce · ogment servers --json
64
- ```
61
+ **`ogment --agentId <id> servers`** — List servers the agent has access to. Shows descriptions, tool counts.
65
62
 
66
- **`ogment call <server> <tool> [args]`** Call any tool. Returns JSON:
67
- ```
68
- ogment call salesforce query_accounts '{"limit":5}'
69
- ogment call my-api get_customers '{"status":"active"}'
63
+ **`ogment --agentId <id> servers <path>`**Inspect a server's tools with parameters, types, and descriptions.
64
+
65
+ **`ogment --agentId <id> call <server> <tool> [args]`** — Call any tool. Returns clean JSON:
66
+ ```bash
67
+ ogment --agentId work call salesforce query_accounts '{"limit":5}'
68
+ ogment --agentId work call my-api get_customers '{"status":"active"}'
70
69
  ```
71
70
 
72
71
  **`ogment logout`** — Revoke token. All agent access stops immediately.
73
72
 
74
- ## For Agent Developers
73
+ ## Agent Identity
75
74
 
76
- The CLI is designed for AI agents calling via shell exec. Every command supports `--json`. A typical agent workflow:
75
+ Every `servers` and `call` command requires `--agentId`. This identifies which agent is making the request, and Ogment enforces per-agent permissions:
77
76
 
78
77
  ```bash
79
- ogment servers --json # discover
80
- ogment servers salesforce --json # inspect tools
81
- ogment call salesforce query_accounts '{"status":"open"}' # call
78
+ # Agent "work" — sees only servers it's permitted for
79
+ ogment --agentId work servers
80
+
81
+ # Agent "work" — tool call is permission-checked and logged
82
+ ogment --agentId work call salesforce query_accounts '{"limit":5}'
82
83
  ```
83
84
 
84
- Publish a skill on [ClawHub](https://clawhub.ai) that teaches the agent these three commands and it has access to every integration on Ogment.
85
+ Agents can also be identified via the `OGMENT_AGENT_ID` environment variable (set by OpenClaw per-agent skill config). The `--agentId` flag takes priority.
86
+
87
+ **Server access types:**
88
+ - **Public servers** — accessible to all agents (default)
89
+ - **Restricted servers** — only agents with explicit permission can access
90
+
91
+ Configure agent permissions in the Ogment dashboard under the **Agents** tab.
85
92
 
86
93
  ## Why Not Just Call APIs Directly?
87
94
 
88
95
  | | **Direct API + CLI** | **Through Ogment** |
89
96
  |---|---|---|
90
97
  | **Credentials** | API keys in env vars and config files. Every agent has a copy. | Credentials never leave Ogment. Agents get scoped, revocable tokens. |
91
- | **Permissions** | All-or-nothing. Agent has the key, agent can do anything. | Per-tool. Read-only, write with approval, or blocked. |
98
+ | **Permissions** | All-or-nothing. Agent has the key, agent can do anything. | Per-agent, per-server. Public or restricted with full/read-only modes. |
92
99
  | **Human approval** | Not possible. | Built-in. Sensitive tools require human sign-off. |
93
100
  | **Team sharing** | Each person sets up their own keys. Onboarding = sharing secrets. | One workspace. Invite a teammate, same servers, same rules, no secrets shared. |
94
- | **Multi-agent** | Each AI client needs its own keys and config. Switch tools? Redo everything. | Define once. Every AI client connects to the same setup. |
101
+ | **Multi-agent** | Each AI client needs its own keys and config. Switch tools? Redo everything. | Define once. Every agent connects to the same setup with its own permissions. |
95
102
  | **Workflows** | Rebuild skills, prompts, and tool chains for each AI client. | Author once as MCP servers, Skills, or Claude Plugins — works across every client. |
96
- | **Audit** | Hope you added logging. | Every call logged — user, agent, timestamp, args, result. |
103
+ | **Audit** | Hope you added logging. | Every call logged — agent, user, server, tool, timestamp, args, result. |
97
104
  | **Revocation** | Rotate the API key, break everything that uses it. | Revoke one agent's token. Everything else keeps working. |
98
105
 
99
106
  **The CLI and MCP are the interface. Ogment is the infrastructure.**
@@ -104,7 +111,7 @@ Define your integrations, permissions, and workflows once — connect from any A
104
111
 
105
112
  | Client | How |
106
113
  |---|---|
107
- | **OpenClaw** | Agent calls `ogment` via `exec` tool |
114
+ | **OpenClaw** | Agent calls `ogment` via `exec` tool with `--agentId` |
108
115
  | **Cursor** | MCP config with Ogment endpoint |
109
116
  | **Claude Desktop** | MCP config with Ogment endpoint |
110
117
  | **ChatGPT** | MCP config with Ogment endpoint |
@@ -117,6 +124,7 @@ Same servers, same tools, same permissions — regardless of which AI client you
117
124
  | Variable | Default | Description |
118
125
  |---|---|---|
119
126
  | `OGMENT_BASE_URL` | `https://dashboard.ogment.ai` | Ogment platform URL |
127
+ | `OGMENT_AGENT_ID` | — | Agent identity (fallback for `--agentId` flag) |
120
128
 
121
129
  ## License
122
130
 
package/dist/api.d.ts CHANGED
@@ -5,7 +5,9 @@ export interface OgmentServer {
5
5
  id: string;
6
6
  name: string;
7
7
  path: string;
8
+ description: string | null;
8
9
  enabled: boolean;
10
+ agentRestricted: boolean;
9
11
  }
10
12
  export interface OgmentOrg {
11
13
  orgId: string;
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAMD,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,qBAY1C"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAMD,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,qBAgB1C"}
package/dist/api.js CHANGED
@@ -2,11 +2,16 @@
2
2
  * Ogment API client — calls the backend REST endpoints.
3
3
  */
4
4
  import { OGMENT_BASE_URL } from './config.js';
5
+ import { getAgentId } from './mcp-client.js';
5
6
  // ---------------------------------------------------------------------------
6
7
  // /me — user identity + server list across all orgs
7
8
  // ---------------------------------------------------------------------------
8
9
  export async function fetchMe(token) {
9
- const res = await fetch(`${OGMENT_BASE_URL}/api/v1/mcp-auth/me`, {
10
+ const url = new URL(`${OGMENT_BASE_URL}/api/v1/mcp-auth/me`);
11
+ const agentId = getAgentId();
12
+ if (agentId)
13
+ url.searchParams.set('agentId', agentId);
14
+ const res = await fetch(url.toString(), {
10
15
  headers: { Authorization: `Bearer ${token}` },
11
16
  });
12
17
  if (!res.ok) {
package/dist/cli.d.ts CHANGED
@@ -2,10 +2,13 @@
2
2
  /**
3
3
  * Ogment CLI — Secure your AI agents' SaaS credentials.
4
4
  *
5
- * ogment login <org/server> Authenticate and configure servers
6
- * ogment servers [path] List servers / inspect tools
7
- * ogment call <server> <tool> Call a tool (returns JSON)
8
- * ogment logout Revoke token and delete credentials
5
+ * ogment login Authenticate with Ogment
6
+ * ogment servers [path] List servers / inspect tools
7
+ * ogment call <server> <tool> [args] Call a tool (returns JSON)
8
+ * ogment logout Revoke token and delete credentials
9
+ *
10
+ * Global options:
11
+ * --agent <id> Identify as an agent (enforces per-agent permissions)
9
12
  */
10
13
  export {};
11
14
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
package/dist/cli.js CHANGED
@@ -2,10 +2,13 @@
2
2
  /**
3
3
  * Ogment CLI — Secure your AI agents' SaaS credentials.
4
4
  *
5
- * ogment login <org/server> Authenticate and configure servers
6
- * ogment servers [path] List servers / inspect tools
7
- * ogment call <server> <tool> Call a tool (returns JSON)
8
- * ogment logout Revoke token and delete credentials
5
+ * ogment login Authenticate with Ogment
6
+ * ogment servers [path] List servers / inspect tools
7
+ * ogment call <server> <tool> [args] Call a tool (returns JSON)
8
+ * ogment logout Revoke token and delete credentials
9
+ *
10
+ * Global options:
11
+ * --agent <id> Identify as an agent (enforces per-agent permissions)
9
12
  */
10
13
  import { Command } from 'commander';
11
14
  import { callCommand } from './commands/call.js';
@@ -13,12 +16,20 @@ import { loginCommand } from './commands/login.js';
13
16
  import { logoutCommand } from './commands/logout.js';
14
17
  import { serversCommand } from './commands/servers.js';
15
18
  import { VERSION } from './config.js';
19
+ import { setAgentId } from './mcp-client.js';
16
20
  import { BOLD, BRAND, DIM, SYM } from './ui.js';
17
21
  const program = new Command();
18
22
  program
19
23
  .name('ogment')
20
24
  .description('Ogment CLI — secure your AI agents\' SaaS credentials')
21
- .version(VERSION);
25
+ .version(VERSION)
26
+ .option('--agentId <id>', 'Identify as an agent (enforces per-agent permissions)')
27
+ .hook('preAction', (thisCommand) => {
28
+ const opts = thisCommand.opts();
29
+ if (opts.agentId) {
30
+ setAgentId(opts.agentId);
31
+ }
32
+ });
22
33
  // ── login ──────────────────────────────────────────────────────────────────
23
34
  program
24
35
  .command('login')
@@ -61,11 +72,15 @@ program.action(() => {
61
72
  console.log(` ${BOLD('call')} ${DIM('Call a tool on a server (returns JSON)')}`);
62
73
  console.log(` ${BOLD('logout')} ${DIM('Revoke token and delete credentials')}`);
63
74
  console.log();
75
+ console.log(` ${BOLD('Global options:')}`);
76
+ console.log();
77
+ console.log(` ${BOLD('--agentId <id>')} ${DIM('Identify as an agent (per-agent permissions)')}`);
78
+ console.log();
64
79
  console.log(` ${BOLD('Quick start:')}`);
65
80
  console.log();
66
81
  console.log(` ${DIM('$')} ogment login`);
67
- console.log(` ${DIM('$')} ogment servers`);
68
- console.log(` ${DIM('$')} ogment call ${DIM('<server> <tool> [args-json]')}`);
82
+ console.log(` ${DIM('$')} ogment --agentId work servers`);
83
+ console.log(` ${DIM('$')} ogment --agentId work call ${DIM('<server> <tool> [args]')}`);
69
84
  console.log();
70
85
  console.log(` ${SYM.lock} Your real API credentials ${BOLD('never leave Ogment')}.`);
71
86
  console.log(` Agents get scoped, revocable tokens. Revoke anytime.`);
@@ -2,8 +2,8 @@
2
2
  * `ogment call <server> <tool> [args]` — Invoke a tool on an Ogment server.
3
3
  *
4
4
  * Usage:
5
- * ogment call ecommerce-api get__health
6
- * ogment call ecommerce-api get__api_products_ '{"limit":2}'
5
+ * ogment --agentId <id> call ecommerce-api get__health
6
+ * ogment --agentId <id> call ecommerce-api get__api_products_ '{"limit":2}'
7
7
  *
8
8
  * Always outputs JSON (designed for agent consumption via exec).
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,iBAoC7B"}
1
+ {"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,iBA8C7B"}
@@ -2,23 +2,26 @@
2
2
  * `ogment call <server> <tool> [args]` — Invoke a tool on an Ogment server.
3
3
  *
4
4
  * Usage:
5
- * ogment call ecommerce-api get__health
6
- * ogment call ecommerce-api get__api_products_ '{"limit":2}'
5
+ * ogment --agentId <id> call ecommerce-api get__health
6
+ * ogment --agentId <id> call ecommerce-api get__api_products_ '{"limit":2}'
7
7
  *
8
8
  * Always outputs JSON (designed for agent consumption via exec).
9
9
  */
10
10
  import { requireCredentials } from '../auth.js';
11
11
  import { fetchMe } from '../api.js';
12
- import { callTool } from '../mcp-client.js';
12
+ import { callTool, requireAgentId } from '../mcp-client.js';
13
13
  export async function callCommand(serverPath, toolName, argsJson) {
14
+ const agentId = requireAgentId();
14
15
  const creds = requireCredentials();
15
16
  const me = await fetchMe(creds.accessToken);
16
17
  // Find the server across all orgs
17
18
  const allServers = me.orgs.flatMap((org) => org.servers.map((s) => ({ ...s, orgSlug: org.orgSlug })));
18
19
  const server = allServers.find((s) => s.path === serverPath);
19
20
  if (!server) {
20
- const available = allServers.map((s) => s.path).join(', ');
21
- throw new Error(`Server "${serverPath}" not found. Available: ${available || 'none'}`);
21
+ const available = allServers.map((s) => s.path);
22
+ throw new Error(`Server "${serverPath}" not found.\n` +
23
+ ` Available servers: ${available.join(', ') || 'none'}\n` +
24
+ ` Run: ogment --agentId ${agentId} servers`);
22
25
  }
23
26
  let args = {};
24
27
  if (argsJson) {
@@ -26,9 +29,14 @@ export async function callCommand(serverPath, toolName, argsJson) {
26
29
  args = JSON.parse(argsJson);
27
30
  }
28
31
  catch {
29
- throw new Error(`Invalid JSON arguments: ${argsJson}`);
32
+ throw new Error(`Invalid JSON arguments: ${argsJson}\n` +
33
+ ` Arguments must be a valid JSON string.\n` +
34
+ ` Example: ogment --agentId ${agentId} call ${serverPath} ${toolName} '{"key": "value"}'`);
30
35
  }
31
36
  }
32
37
  const result = await callTool(server.orgSlug, serverPath, creds.accessToken, toolName, args);
33
- console.log(JSON.stringify(result, null, 2));
38
+ // Prefer structuredContent (parsed JSON), fall back to text content
39
+ const output = result.structuredContent
40
+ ?? JSON.parse(result.content?.[0]?.text ?? '{}');
41
+ console.log(JSON.stringify(output, null, 2));
34
42
  }
@@ -4,10 +4,10 @@
4
4
  * Fetches the live server list from /me (auto-refreshes on every call).
5
5
  *
6
6
  * Usage:
7
- * ogment servers List all servers across all orgs
8
- * ogment servers <path> Show server details + tool list
9
- * ogment servers --json Machine-readable output
10
- * ogment servers <path> --json Machine-readable tool list
7
+ * ogment --agentId <id> servers List all servers
8
+ * ogment --agentId <id> servers <path> Show tools with parameters
9
+ * ogment --agentId <id> servers --json Machine-readable output
10
+ * ogment --agentId <id> servers <path> --json Machine-readable with schemas
11
11
  */
12
12
  export declare function serversCommand(serverPath: string | undefined, opts: {
13
13
  json?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"servers.d.ts","sourceRoot":"","sources":["../../src/commands/servers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,iBA6EzB"}
1
+ {"version":3,"file":"servers.d.ts","sourceRoot":"","sources":["../../src/commands/servers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA8CH,wBAAsB,cAAc,CAClC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,iBA0FzB"}
@@ -4,16 +4,42 @@
4
4
  * Fetches the live server list from /me (auto-refreshes on every call).
5
5
  *
6
6
  * Usage:
7
- * ogment servers List all servers across all orgs
8
- * ogment servers <path> Show server details + tool list
9
- * ogment servers --json Machine-readable output
10
- * ogment servers <path> --json Machine-readable tool list
7
+ * ogment --agentId <id> servers List all servers
8
+ * ogment --agentId <id> servers <path> Show tools with parameters
9
+ * ogment --agentId <id> servers --json Machine-readable output
10
+ * ogment --agentId <id> servers <path> --json Machine-readable with schemas
11
11
  */
12
12
  import { requireCredentials } from '../auth.js';
13
13
  import { fetchMe } from '../api.js';
14
- import { fetchTools } from '../mcp-client.js';
15
- import { blank, BOLD, DIM, GREEN, heading, line, SYM } from '../ui.js';
14
+ import { fetchTools, requireAgentId } from '../mcp-client.js';
15
+ import { blank, BOLD, DIM, GREEN, heading, line, SYM, YELLOW } from '../ui.js';
16
+ function formatType(prop) {
17
+ if (prop.enum)
18
+ return prop.enum.join(' | ');
19
+ if (prop.type === 'array' && prop.items?.type)
20
+ return `${prop.items.type}[]`;
21
+ return prop.type ?? 'unknown';
22
+ }
23
+ function renderParams(schema) {
24
+ const properties = (schema.properties ?? {});
25
+ const required = new Set((schema.required ?? []));
26
+ const entries = Object.entries(properties);
27
+ if (entries.length === 0) {
28
+ line(` ${DIM('No parameters')}`, 0);
29
+ return;
30
+ }
31
+ for (const [name, prop] of entries) {
32
+ const req = required.has(name) ? YELLOW('required') : DIM('optional');
33
+ const type = DIM(`(${formatType(prop)})`);
34
+ const desc = prop.description ? ` — ${prop.description}` : '';
35
+ line(` ${BOLD(name)} ${type} ${req}${desc}`, 0);
36
+ }
37
+ }
38
+ // ---------------------------------------------------------------------------
39
+ // Command
40
+ // ---------------------------------------------------------------------------
16
41
  export async function serversCommand(serverPath, opts) {
42
+ const agentId = requireAgentId();
17
43
  const creds = requireCredentials();
18
44
  const me = await fetchMe(creds.accessToken);
19
45
  // Flatten all servers with their org slug
@@ -24,27 +50,30 @@ export async function serversCommand(serverPath, opts) {
24
50
  console.log(JSON.stringify(allServers, null, 2));
25
51
  return;
26
52
  }
27
- heading('Your servers');
53
+ heading(`Servers for agent "${agentId}"`);
28
54
  if (allServers.length === 0) {
29
- line(`${DIM('No servers found. Create one in the Ogment dashboard.')}`);
55
+ line(`No servers available for agent "${agentId}".`);
56
+ line(`${DIM('Grant access in the Ogment dashboard under the Agents tab.')}`);
30
57
  blank();
31
58
  return;
32
59
  }
33
60
  for (const server of allServers) {
34
- const status = server.enabled ? GREEN('enabled') : DIM('disabled');
35
- line(`${SYM.bullet} ${BOLD(server.path.padEnd(24))} ${server.name.padEnd(24)} ${DIM(server.orgSlug.padEnd(16))} ${status}`);
61
+ const desc = server.description ? `\n ${DIM(server.description)}` : '';
62
+ line(`${SYM.bullet} ${BOLD(server.path.padEnd(24))} ${server.name}${desc}`);
36
63
  }
37
64
  blank();
38
- line(`${DIM('Inspect a server:')} ${BOLD('ogment servers <path>')}`);
39
- line(`${DIM('Call a tool:')} ${BOLD('ogment call <server> <tool> [args]')}`);
65
+ line(`${DIM('Inspect tools:')} ogment --agentId ${agentId} servers <path>`);
66
+ line(`${DIM('Call a tool:')} ogment --agentId ${agentId} call <server> <tool> [args]`);
40
67
  blank();
41
68
  return;
42
69
  }
43
70
  // ── Server detail + tools ──────────────────────────────────────────────
44
71
  const server = allServers.find((s) => s.path === serverPath);
45
72
  if (!server) {
46
- const available = allServers.map((s) => s.path).join(', ');
47
- throw new Error(`Server "${serverPath}" not found. Available: ${available || 'none'}`);
73
+ const available = allServers.map((s) => s.path);
74
+ throw new Error(`Server "${serverPath}" not found.\n` +
75
+ ` Available servers: ${available.join(', ') || 'none'}\n` +
76
+ ` Run: ogment --agentId ${agentId} servers`);
48
77
  }
49
78
  const tools = await fetchTools(server.orgSlug, serverPath, creds.accessToken);
50
79
  if (opts.json) {
@@ -54,19 +83,28 @@ export async function serversCommand(serverPath, opts) {
54
83
  enabled: server.enabled,
55
84
  orgSlug: server.orgSlug,
56
85
  toolCount: tools.length,
57
- tools: tools.map((t) => ({ name: t.name, description: t.description })),
86
+ tools: tools.map((t) => ({
87
+ name: t.name,
88
+ description: t.description,
89
+ inputSchema: t.inputSchema,
90
+ })),
58
91
  }, null, 2));
59
92
  return;
60
93
  }
61
94
  heading(`${server.name} (${server.path})`);
62
- line(`${BOLD('Org:')} ${server.orgSlug}`);
95
+ if (server.description) {
96
+ line(DIM(server.description));
97
+ }
63
98
  line(`${BOLD('Tools:')} ${tools.length}`);
64
99
  blank();
65
100
  for (const tool of tools) {
66
- const desc = tool.description ? DIM(` — ${tool.description}`) : '';
67
- line(` ${GREEN(tool.name)}${desc}`);
101
+ const desc = tool.description
102
+ ? `\n ${DIM(tool.description.split('\n')[0])}`
103
+ : '';
104
+ line(` ${GREEN(tool.name)}${desc}`, 0);
105
+ renderParams(tool.inputSchema);
106
+ blank();
68
107
  }
69
- blank();
70
- line(`${DIM('Call a tool:')} ${BOLD(`ogment call ${serverPath} <tool> [args]`)}`);
108
+ line(`${DIM('Call a tool:')} ogment --agentId ${agentId} call ${serverPath} <tool> '{"param": "value"}'`);
71
109
  blank();
72
110
  }
package/dist/config.d.ts CHANGED
@@ -19,5 +19,5 @@ export declare const CONFIG_DIR: string;
19
19
  export declare const CREDENTIALS_PATH: string;
20
20
  export declare const CLI_CLIENT_NAME = "Ogment CLI";
21
21
  export declare const CLI_REDIRECT_HOST = "127.0.0.1";
22
- export declare const VERSION = "0.2.2";
22
+ export declare const VERSION = "0.2.4";
23
23
  //# sourceMappingURL=config.d.ts.map
package/dist/config.js CHANGED
@@ -33,4 +33,4 @@ export const CLI_REDIRECT_HOST = '127.0.0.1';
33
33
  // ---------------------------------------------------------------------------
34
34
  // Version
35
35
  // ---------------------------------------------------------------------------
36
- export const VERSION = '0.2.2';
36
+ export const VERSION = '0.2.4';
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * MCP HTTP client — sends JSON-RPC requests to Ogment's MCP proxy.
3
3
  *
4
- * Shared by both the stdio bridge (serve.ts) and CLI commands (servers, call).
4
+ * Handles the MCP Streamable HTTP transport:
5
+ * 1. Initialize session on first request to a server
6
+ * 2. Cache session ID (mcp-session-id header)
7
+ * 3. Re-initialize automatically if session expires
5
8
  */
6
9
  export interface McpTool {
7
10
  name: string;
@@ -16,6 +19,12 @@ interface McpToolCallResult {
16
19
  structuredContent?: unknown;
17
20
  isError?: boolean;
18
21
  }
22
+ /** Set the agent ID for all subsequent MCP requests. */
23
+ export declare function setAgentId(id: string | undefined): void;
24
+ /** Get the current agent ID (flag > env var). */
25
+ export declare function getAgentId(): string | undefined;
26
+ /** Require an agent ID — throws if not set. */
27
+ export declare function requireAgentId(): string;
19
28
  /** Fetch the list of tools available on a server. */
20
29
  export declare function fetchTools(orgSlug: string, serverPath: string, token: string): Promise<McpTool[]>;
21
30
  /** Call a tool on a server and return the result. */
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,iBAAiB;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAmDD,qDAAqD;AACrD,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAKlF;AAED,qDAAqD;AACrD,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,8BAM9B"}
1
+ {"version":3,"file":"mcp-client.d.ts","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,UAAU,iBAAiB;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAeD,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,QAEhD;AAED,iDAAiD;AACjD,wBAAgB,UAAU,uBAEzB;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,WAQ7B;AAuID,qDAAqD;AACrD,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAKlF;AAED,qDAAqD;AACrD,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,8BAM9B"}
@@ -1,14 +1,101 @@
1
1
  /**
2
2
  * MCP HTTP client — sends JSON-RPC requests to Ogment's MCP proxy.
3
3
  *
4
- * Shared by both the stdio bridge (serve.ts) and CLI commands (servers, call).
4
+ * Handles the MCP Streamable HTTP transport:
5
+ * 1. Initialize session on first request to a server
6
+ * 2. Cache session ID (mcp-session-id header)
7
+ * 3. Re-initialize automatically if session expires
5
8
  */
6
- import { MCP_ENDPOINT } from './config.js';
9
+ import { MCP_ENDPOINT, VERSION } from './config.js';
7
10
  // ---------------------------------------------------------------------------
8
- // Core JSON-RPC request
11
+ // Session management
12
+ // ---------------------------------------------------------------------------
13
+ /** Per-endpoint session cache (endpoint URL → session ID). */
14
+ const sessions = new Map();
15
+ /**
16
+ * Global agent ID — set via --agent flag or OGMENT_AGENT_ID env var.
17
+ * When set, the proxy enforces per-agent permissions.
18
+ */
19
+ let _agentId;
20
+ /** Set the agent ID for all subsequent MCP requests. */
21
+ export function setAgentId(id) {
22
+ _agentId = id;
23
+ }
24
+ /** Get the current agent ID (flag > env var). */
25
+ export function getAgentId() {
26
+ return _agentId ?? process.env.OGMENT_AGENT_ID;
27
+ }
28
+ /** Require an agent ID — throws if not set. */
29
+ export function requireAgentId() {
30
+ const id = getAgentId();
31
+ if (!id) {
32
+ throw new Error('--agentId is required. Example: ogment --agentId <your-agent> servers');
33
+ }
34
+ return id;
35
+ }
36
+ /** Build common headers for MCP requests. */
37
+ function baseHeaders(token) {
38
+ const h = {
39
+ 'Content-Type': 'application/json',
40
+ Authorization: `Bearer ${token}`,
41
+ };
42
+ const agentId = getAgentId();
43
+ if (agentId)
44
+ h['x-ogment-agent-id'] = agentId;
45
+ return h;
46
+ }
47
+ /** Initialize an MCP session and return the session ID. */
48
+ async function initializeSession(endpoint, token) {
49
+ const res = await fetch(endpoint, {
50
+ method: 'POST',
51
+ headers: baseHeaders(token),
52
+ body: JSON.stringify({
53
+ jsonrpc: '2.0',
54
+ method: 'initialize',
55
+ id: Date.now(),
56
+ params: {
57
+ protocolVersion: '2024-11-05',
58
+ capabilities: {},
59
+ clientInfo: { name: 'ogment-cli', version: VERSION },
60
+ },
61
+ }),
62
+ });
63
+ if (!res.ok) {
64
+ const text = await res.text();
65
+ // Missing OAuth connections — user needs to connect the integration in the dashboard
66
+ if (text.includes('Missing required external MCP connections')) {
67
+ const match = text.match(/connections:\s*(.+?)"/);
68
+ const connections = match?.[1] ?? 'unknown';
69
+ throw new Error(`This server requires OAuth connections that are not set up: ${connections}\n` +
70
+ ` Go to the Ogment dashboard and connect the required integration.`);
71
+ }
72
+ // Agent permission denied — agent needs to be granted access
73
+ if (text.includes('AgentPermissionDenied') || text.includes('does not have access')) {
74
+ const agentId = getAgentId() ?? 'unknown';
75
+ throw new Error(`Agent "${agentId}" does not have access to this server.\n` +
76
+ ` Grant access in the Ogment dashboard under the Agents tab.`);
77
+ }
78
+ const sanitized = text.length > 200 ? text.slice(0, 200) + '…' : text;
79
+ throw new Error(`MCP request failed (${res.status}): ${sanitized}`);
80
+ }
81
+ return res.headers.get('mcp-session-id');
82
+ }
83
+ /** Ensure a session exists for the given endpoint, initializing if needed. */
84
+ async function ensureSession(endpoint, token) {
85
+ const existing = sessions.get(endpoint);
86
+ if (existing)
87
+ return existing;
88
+ const sessionId = await initializeSession(endpoint, token);
89
+ if (sessionId)
90
+ sessions.set(endpoint, sessionId);
91
+ return sessionId;
92
+ }
93
+ // ---------------------------------------------------------------------------
94
+ // Core JSON-RPC request (with session handling)
9
95
  // ---------------------------------------------------------------------------
10
96
  async function mcpRequest(orgSlug, serverPath, token, method, params) {
11
97
  const endpoint = MCP_ENDPOINT(orgSlug, serverPath);
98
+ const sessionId = await ensureSession(endpoint, token);
12
99
  const body = {
13
100
  jsonrpc: '2.0',
14
101
  method,
@@ -16,17 +103,31 @@ async function mcpRequest(orgSlug, serverPath, token, method, params) {
16
103
  };
17
104
  if (params)
18
105
  body.params = params;
19
- const res = await fetch(endpoint, {
106
+ const headers = baseHeaders(token);
107
+ if (sessionId)
108
+ headers['mcp-session-id'] = sessionId;
109
+ let res = await fetch(endpoint, {
20
110
  method: 'POST',
21
- headers: {
22
- 'Content-Type': 'application/json',
23
- Authorization: `Bearer ${token}`,
24
- },
111
+ headers,
25
112
  body: JSON.stringify(body),
26
113
  });
114
+ // Session expired — re-initialize once and retry
115
+ if (res.status === 404 || res.status === 400) {
116
+ sessions.delete(endpoint);
117
+ const newSessionId = await initializeSession(endpoint, token);
118
+ if (newSessionId)
119
+ sessions.set(endpoint, newSessionId);
120
+ const retryHeaders = baseHeaders(token);
121
+ if (newSessionId)
122
+ retryHeaders['mcp-session-id'] = newSessionId;
123
+ res = await fetch(endpoint, {
124
+ method: 'POST',
125
+ headers: retryHeaders,
126
+ body: JSON.stringify(body),
127
+ });
128
+ }
27
129
  if (!res.ok) {
28
130
  const text = await res.text();
29
- // Truncate error bodies to avoid leaking server internals into agent logs
30
131
  const sanitized = text.length > 200 ? text.slice(0, 200) + '…' : text;
31
132
  throw new Error(`MCP request failed (${res.status}): ${sanitized}`);
32
133
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ogment",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Ogment Vault CLI — secure your AI agents' SaaS credentials",
5
5
  "type": "module",
6
6
  "bin": {
@@ -48,6 +48,7 @@
48
48
  "dependencies": {
49
49
  "chalk": "^5.4.1",
50
50
  "commander": "^13.1.0",
51
+ "ogment": "^0.2.3",
51
52
  "open": "^10.2.0",
52
53
  "ora": "^8.2.0"
53
54
  },