tomo-ai 0.6.0 → 0.7.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/CHANGELOG.md +13 -0
- package/README.md +42 -2
- package/defaults/skills/system/CONFIG.md +20 -3
- package/defaults/skills/system/SKILL.md +5 -0
- package/dist/agent/sdk-options.d.ts +9 -6
- package/dist/agent/sdk-options.d.ts.map +1 -1
- package/dist/agent/sdk-options.js +8 -2
- package/dist/agent/sdk-options.js.map +1 -1
- package/dist/agent.d.ts +6 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +105 -5
- package/dist/agent.js.map +1 -1
- package/dist/channels/telegram.js +1 -1
- package/dist/channels/telegram.js.map +1 -1
- package/dist/cli/config/model.js +1 -1
- package/dist/cli/config/model.js.map +1 -1
- package/dist/cli/config/shared.d.ts.map +1 -1
- package/dist/cli/config/shared.js +6 -2
- package/dist/cli/config/shared.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +4 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -1
- package/dist/config.js.map +1 -1
- package/dist/mcp/external-config.d.ts +18 -0
- package/dist/mcp/external-config.d.ts.map +1 -0
- package/dist/mcp/external-config.js +121 -0
- package/dist/mcp/external-config.js.map +1 -0
- package/dist/mcp/oauth.d.ts +45 -0
- package/dist/mcp/oauth.d.ts.map +1 -0
- package/dist/mcp/oauth.js +366 -0
- package/dist/mcp/oauth.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 (2026-05-29)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **External MCP server support with harness-managed OAuth** (#114, #115). Users can declare additional MCP servers in `~/.tomo/config.json` under `mcpServers` (also accepted as `mcp.servers`) for stdio, HTTP, streamable HTTP, and SSE transports; optional `mcpAllowedTools` restricts the surface, otherwise all configured tools are exposed. For OAuth-protected remotes (GitHub Copilot MCP, Robinhood MCP, etc.), a per-server `oauth` block drives authorization-code + PKCE with dynamic client registration and a localhost callback; the harness refreshes near-expiry tokens and injects `Authorization: Bearer …` into HTTP/SSE headers at session-build time. Tokens live in `~/.tomo/workspace/secrets/mcp-oauth.json` (mode `0600`), kept separate from `keychain.json` so keychain rewrites don't wipe MCP auth, and never reach the agent. Auth failures are isolated per server — a broken server is omitted with a one-line notice and the session still starts. Discovery follows RFC 9728 `WWW-Authenticate` challenges with a `/.well-known/oauth-protected-resource/...` fallback for POST-only endpoints like Robinhood MCP. The bundled `tomo-system` skill now tells the agent that user-configured tools appear as `mcp__<server>__<tool>` and that it should never ask users for tokens.
|
|
8
|
+
- **1M context model options** (#116). New `/model` aliases `sonnet-1m` and `opus-1m` map to `claude-sonnet-4-6[1m]` and `claude-opus-4-8[1m]`. `claude-sonnet-4-6[1m]` is now the default for new/fallback configs. Init/config UI labels, the Telegram command description, README, and `tomo-system/CONFIG.md` are updated to match.
|
|
9
|
+
|
|
10
|
+
## 0.6.1 (2026-05-28)
|
|
11
|
+
|
|
12
|
+
### Other
|
|
13
|
+
|
|
14
|
+
- Default Opus model upgraded from Claude Opus 4.7 to Claude Opus 4.8 (#113).
|
|
15
|
+
|
|
3
16
|
## 0.6.0 (2026-05-26)
|
|
4
17
|
|
|
5
18
|
### Features
|
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ tomo sessions clear # Reset all sessions
|
|
|
58
58
|
| Command | Description |
|
|
59
59
|
|---------|-------------|
|
|
60
60
|
| `/new` | Start a new conversation (resets session) |
|
|
61
|
-
| `/model` | Switch model (sonnet/opus/haiku) |
|
|
61
|
+
| `/model` | Switch model (sonnet/sonnet-1m/opus/opus-1m/haiku) |
|
|
62
62
|
| `/restore` | Restore `config.json` from `config.json.bak` and restart |
|
|
63
63
|
| `/status` | Show session info (model, channel, message count) |
|
|
64
64
|
|
|
@@ -119,6 +119,46 @@ Tomo has access to Claude's built-in tools:
|
|
|
119
119
|
| `send_message` (MCP) | Proactively send a message to another session/identity |
|
|
120
120
|
| `list_sessions` (MCP) | List active identities and group sessions |
|
|
121
121
|
|
|
122
|
+
### External MCP Servers
|
|
123
|
+
|
|
124
|
+
Add remote or local MCP servers directly to `~/.tomo/config.json` under `mcpServers`. Tomo passes them through to the Claude Agent SDK and, by default, auto-allows all tools from each configured server with `mcp__<server>__*`.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"mcpServers": {
|
|
129
|
+
"docs": {
|
|
130
|
+
"type": "http",
|
|
131
|
+
"url": "https://code.claude.com/docs/mcp"
|
|
132
|
+
},
|
|
133
|
+
"github": {
|
|
134
|
+
"type": "sse",
|
|
135
|
+
"url": "https://api.example.com/mcp/sse",
|
|
136
|
+
"headers": {
|
|
137
|
+
"Authorization": "Bearer ${GITHUB_TOKEN}"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"github-copilot": {
|
|
141
|
+
"type": "http",
|
|
142
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
143
|
+
"oauth": {
|
|
144
|
+
"scopes": ["read:user"],
|
|
145
|
+
"tokenStoreKey": "github-copilot"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"filesystem": {
|
|
149
|
+
"command": "npx",
|
|
150
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "${HOME}/Projects"]
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Use `type: "http"` for streamable HTTP, `type: "sse"` for SSE, or omit `type` for stdio servers. Environment variables in `url`, `headers`, `env`, and `args` are expanded at runtime. To restrict auto-approval, set top-level `mcpAllowedTools`, for example `["mcp__github__list_issues"]`.
|
|
157
|
+
|
|
158
|
+
Remote HTTP/SSE servers can also use harness-managed OAuth by adding an `oauth` block. Tomo discovers the authorization server from the MCP server's RFC 9728 `WWW-Authenticate` challenge unless `authorizationServer` is set explicitly, uses authorization-code + PKCE, registers a dynamic client when needed, stores tokens in `~/.tomo/workspace/secrets/mcp-oauth.json` with mode `0600`, silently refreshes near-expiry tokens, and injects `Authorization: Bearer <token>` into MCP headers. The agent never sees the tokens.
|
|
159
|
+
|
|
160
|
+
If browser login is needed, Tomo forwards the authorize URL to your private chat and waits for the localhost callback before starting the agent session.
|
|
161
|
+
|
|
122
162
|
### Scheduled Tasks
|
|
123
163
|
|
|
124
164
|
Tomo can create scheduled tasks on its own — just ask "remind me in 30 minutes to stretch" or "check the weather every morning at 9am." Supports one-shot reminders, recurring intervals, and cron expressions.
|
|
@@ -177,7 +217,7 @@ Run `tomo config` for interactive setup, or edit `~/.tomo/config.json` directly:
|
|
|
177
217
|
"replyPolicy": "last-active"
|
|
178
218
|
}
|
|
179
219
|
],
|
|
180
|
-
"model": "claude-sonnet-4-6",
|
|
220
|
+
"model": "claude-sonnet-4-6[1m]",
|
|
181
221
|
"maxTurns": 50,
|
|
182
222
|
"saveInboundImages": true,
|
|
183
223
|
"groupSecret": "tomo-xxxxxxxx",
|
|
@@ -8,7 +8,7 @@ Before direct edits, copy `~/.tomo/config.json` to `~/.tomo/config.json.bak`. Ch
|
|
|
8
8
|
|
|
9
9
|
```json
|
|
10
10
|
{
|
|
11
|
-
"model": "claude-sonnet-4-6",
|
|
11
|
+
"model": "claude-sonnet-4-6[1m]",
|
|
12
12
|
"city": "Seattle",
|
|
13
13
|
"continuity": false,
|
|
14
14
|
"groupSecret": "tomo-a1b2c3d4",
|
|
@@ -36,9 +36,23 @@ Before direct edits, copy `~/.tomo/config.json` to `~/.tomo/config.json.bak`. Ch
|
|
|
36
36
|
}
|
|
37
37
|
],
|
|
38
38
|
"sessionModelOverrides": {
|
|
39
|
-
"dm:alice": "claude-opus-4-
|
|
39
|
+
"dm:alice": "claude-opus-4-8[1m]"
|
|
40
40
|
},
|
|
41
41
|
"maxTurns": 50,
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"github-copilot": {
|
|
44
|
+
"type": "http",
|
|
45
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
46
|
+
"oauth": {
|
|
47
|
+
"scopes": ["read:user"],
|
|
48
|
+
"tokenStoreKey": "github-copilot"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"filesystem": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "${HOME}/Projects"]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
42
56
|
"lcm": {
|
|
43
57
|
"nudgeAtPct": 70,
|
|
44
58
|
"nudgeResetPct": 60,
|
|
@@ -52,7 +66,7 @@ Before direct edits, copy `~/.tomo/config.json` to `~/.tomo/config.json.bak`. Ch
|
|
|
52
66
|
|
|
53
67
|
| Field | Type | Allowed values / notes |
|
|
54
68
|
|---|---|---|
|
|
55
|
-
| `model` | string | `claude-sonnet-4-6`, `claude-opus-4-
|
|
69
|
+
| `model` | string | `claude-sonnet-4-6[1m]`, `claude-sonnet-4-6`, `claude-opus-4-8[1m]`, `claude-opus-4-8`, `claude-haiku-4-5`. Default model for every session. |
|
|
56
70
|
| `city` | string \| null | Any city name (e.g. `"Seattle"`). Used for weather in continuity pings. `null` or missing = no weather. |
|
|
57
71
|
| `continuity` | boolean | `true` / `false`. Enables periodic proactive heartbeats. Off by default. |
|
|
58
72
|
| `groupSecret` | string \| null | Passphrase users send in a group chat to activate Tomo there. `null` disables group chats entirely. |
|
|
@@ -68,6 +82,9 @@ Before direct edits, copy `~/.tomo/config.json` to `~/.tomo/config.json.bak`. Ch
|
|
|
68
82
|
| `identities[].replyPolicy` | string | `"last-active"` (reply on whichever channel the identity last messaged from) or a fixed channel name like `"telegram"` / `"imessage"` (always reply there). |
|
|
69
83
|
| `sessionModelOverrides` | object | `{ sessionKey: modelId }` — per-session model override, takes precedence over top-level `model`. Keys are session keys (`dm:alice`, `telegram:12345`, etc.). Written by `/model <name>` for the current chat and by the `tomo config` Sessions menu. |
|
|
70
84
|
| `maxTurns` | number | Max agent turns per single user message (one turn ≈ one tool-use round). Default `50`. Raise if you see "max turns exceeded" on long tool chains. |
|
|
85
|
+
| `mcpServers` | object | External MCP servers keyed by server name. Supports stdio (`command`, `args`, `env`), HTTP (`type: "http"`, `url`, `headers`), and SSE (`type: "sse"`, `url`, `headers`). Environment variables like `${HOME}` expand in `url`, `headers`, `env`, and `args`. |
|
|
86
|
+
| `mcpServers.<name>.oauth` | object | Optional harness-managed OAuth for HTTP/SSE MCP servers. Supports `authorizationServer` (optional; omitted = discover from the MCP server), `clientId` (optional if dynamic registration is available), `scopes`, `tokenStoreKey`, `redirectUri`, and `clientName`. Tokens are stored outside agent context in `workspace/secrets/mcp-oauth.json` (`0600`). |
|
|
87
|
+
| `mcpAllowedTools` | string[] | Auto-allowed external MCP tools. If omitted, Tomo defaults to `mcp__<server>__*` for every configured external server. Set this to narrow tool access, e.g. `["mcp__github__list_issues"]`. |
|
|
71
88
|
| `lcm.nudgeAtPct` | number | Context-usage % at which the harness nudges the agent to run `tomo lcm daily`. Default `70`. Lower = compact earlier and more often. |
|
|
72
89
|
| `lcm.nudgeResetPct` | number | Hysteresis reset threshold — the "already nudged" flag clears once usage drops below this %. Default `60`. Must be less than `nudgeAtPct`; invalid values fall back to defaults. |
|
|
73
90
|
| `lcm.groupCompactStyle` | string | `"sdk"` (default) or `"lcm"`. `"sdk"` lets the SDK auto-compact group sessions; `"lcm"` opts groups into the same hierarchical LCM flow as DMs (disables SDK auto-compact and fires all three harness nudges: 70% daily, 80% safety net, and the periodic rollup runner). DMs always use LCM regardless. |
|
|
@@ -107,6 +107,11 @@ In-process MCP tools let you message outside the current conversation:
|
|
|
107
107
|
|
|
108
108
|
Pass identity name (`"alice"`) or session key (`"telegram:-1001234567"`) as `target`. Call `list_sessions` first if unsure. Tool result lines (with `is_error` flag) appear in `tomo logs` immediately after the corresponding tool call.
|
|
109
109
|
|
|
110
|
+
### External MCP tools
|
|
111
|
+
Users may configure additional MCP servers in `~/.tomo/config.json`; their tools appear as `mcp__<server>__<tool>`. Use them normally when relevant.
|
|
112
|
+
|
|
113
|
+
OAuth/token handling is harness-managed. Never ask the user for bearer tokens, OAuth codes, refresh tokens, or MCP auth headers. If a configured MCP server needs login, Tomo sends the user a private login link and injects tokens outside your context. If a server is unavailable, continue with other tools and explain the limitation only if it affects the answer.
|
|
114
|
+
|
|
110
115
|
## Skills
|
|
111
116
|
|
|
112
117
|
### Built-in skills (`tomo-*`)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpSdkServerConfigWithInstance } from "@anthropic-ai/claude-agent-sdk";
|
|
1
|
+
import type { ElicitationRequest, ElicitationResult, McpSdkServerConfigWithInstance, McpServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { skillsCanUseTool } from "./permissions.js";
|
|
3
3
|
/** True when the session uses our custom LCM compaction (DMs always; groups
|
|
4
4
|
* unless config.lcm.groupCompactStyle="sdk"). When false, SDK auto-compact is
|
|
@@ -27,13 +27,17 @@ export interface SessionContext {
|
|
|
27
27
|
* config.passiveGroups. Drives the "stay silent unless useful" rule. */
|
|
28
28
|
isPassive: boolean;
|
|
29
29
|
};
|
|
30
|
+
onMcpElicitation?: (request: ElicitationRequest) => Promise<ElicitationResult>;
|
|
30
31
|
}
|
|
31
|
-
export declare function sdkOptions(internalMcpServer: McpSdkServerConfigWithInstance, resumeSessionId?: string, model?: string, sessionContext?: SessionContext, turnBudget?: TurnBudget): {
|
|
32
|
+
export declare function sdkOptions(internalMcpServer: McpSdkServerConfigWithInstance, resumeSessionId?: string, model?: string, sessionContext?: SessionContext, turnBudget?: TurnBudget, externalMcpServersOverride?: Record<string, McpServerConfig>): {
|
|
32
33
|
env?: {
|
|
33
34
|
DISABLE_AUTO_COMPACT: string;
|
|
34
35
|
} | undefined;
|
|
35
36
|
resume?: string | undefined;
|
|
36
37
|
hooks: Record<string, unknown>;
|
|
38
|
+
includePartialMessages: boolean;
|
|
39
|
+
maxTurns: number;
|
|
40
|
+
onElicitation?: ((request: ElicitationRequest) => Promise<ElicitationResult>) | undefined;
|
|
37
41
|
model: string;
|
|
38
42
|
cwd: string;
|
|
39
43
|
systemPrompt: string;
|
|
@@ -52,14 +56,15 @@ export declare function sdkOptions(internalMcpServer: McpSdkServerConfigWithInst
|
|
|
52
56
|
};
|
|
53
57
|
};
|
|
54
58
|
canUseTool: typeof skillsCanUseTool;
|
|
55
|
-
includePartialMessages: boolean;
|
|
56
|
-
maxTurns: number;
|
|
57
59
|
} | {
|
|
58
60
|
env?: {
|
|
59
61
|
DISABLE_AUTO_COMPACT: string;
|
|
60
62
|
} | undefined;
|
|
61
63
|
resume?: string | undefined;
|
|
62
64
|
hooks?: undefined;
|
|
65
|
+
includePartialMessages: boolean;
|
|
66
|
+
maxTurns: number;
|
|
67
|
+
onElicitation?: ((request: ElicitationRequest) => Promise<ElicitationResult>) | undefined;
|
|
63
68
|
model: string;
|
|
64
69
|
cwd: string;
|
|
65
70
|
systemPrompt: string;
|
|
@@ -78,7 +83,5 @@ export declare function sdkOptions(internalMcpServer: McpSdkServerConfigWithInst
|
|
|
78
83
|
};
|
|
79
84
|
};
|
|
80
85
|
canUseTool: typeof skillsCanUseTool;
|
|
81
|
-
includePartialMessages: boolean;
|
|
82
|
-
maxTurns: number;
|
|
83
86
|
};
|
|
84
87
|
//# sourceMappingURL=sdk-options.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk-options.d.ts","sourceRoot":"","sources":["../../src/agent/sdk-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk-options.d.ts","sourceRoot":"","sources":["../../src/agent/sdk-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,8BAA8B,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAM7I,OAAO,EAA2B,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAS7E;;6DAE6D;AAC7D,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;6EAI6E;AAC7E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,cAAc,IAAI,UAAU,CAE3C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAInD;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,KAAK,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB;iFACyE;QACzE,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAChF;AAED,wBAAgB,UAAU,CACxB,iBAAiB,EAAE,8BAA8B,EACjD,eAAe,CAAC,EAAE,MAAM,EACxB,KAAK,CAAC,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,CAAC,EAAE,UAAU,EACvB,0BAA0B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC;;;;;;;;+BAT/B,kBAAkB,KAAK,OAAO,CAAC,iBAAiB,CAAC;;;;;;;;;;;oBA4E7C,CAAC,SAAS,CAAC,EAAE;;;;;;;;;;;;;;;;+BA5EjB,kBAAkB,KAAK,OAAO,CAAC,iBAAiB,CAAC;;;;;;;;;;;oBA4E7C,CAAC,SAAS,CAAC,EAAE;;;;;;;;EA2B/C"}
|
|
@@ -26,7 +26,7 @@ export function resetTurnBudget(b) {
|
|
|
26
26
|
b.fired75 = false;
|
|
27
27
|
b.fired90 = false;
|
|
28
28
|
}
|
|
29
|
-
export function sdkOptions(internalMcpServer, resumeSessionId, model, sessionContext, turnBudget) {
|
|
29
|
+
export function sdkOptions(internalMcpServer, resumeSessionId, model, sessionContext, turnBudget, externalMcpServersOverride) {
|
|
30
30
|
const isGroup = sessionContext ? isGroupSessionKey(sessionContext.sessionKey) : false;
|
|
31
31
|
let systemPrompt = buildSystemPrompt({ isGroup });
|
|
32
32
|
// Inject session context so the agent can use LCM tools
|
|
@@ -57,6 +57,10 @@ export function sdkOptions(internalMcpServer, resumeSessionId, model, sessionCon
|
|
|
57
57
|
}
|
|
58
58
|
systemPrompt += lines.join("\n");
|
|
59
59
|
}
|
|
60
|
+
const configuredMcpServers = Object.fromEntries(Object.entries(config.mcpServers ?? {}).map(([name, entry]) => [name, entry.server]));
|
|
61
|
+
const externalMcpServers = Object.fromEntries(Object.entries(externalMcpServersOverride ?? configuredMcpServers)
|
|
62
|
+
.filter(([name]) => name !== TOMO_INTERNAL_MCP_NAME));
|
|
63
|
+
const externalMcpAllowedTools = Array.isArray(config.mcpAllowedTools) ? config.mcpAllowedTools : [];
|
|
60
64
|
return {
|
|
61
65
|
model: model ?? config.model,
|
|
62
66
|
cwd: config.workspaceDir,
|
|
@@ -74,6 +78,7 @@ export function sdkOptions(internalMcpServer, resumeSessionId, model, sessionCon
|
|
|
74
78
|
`mcp__${TOMO_INTERNAL_MCP_NAME}__schedule_create`,
|
|
75
79
|
`mcp__${TOMO_INTERNAL_MCP_NAME}__schedule_list`,
|
|
76
80
|
`mcp__${TOMO_INTERNAL_MCP_NAME}__schedule_remove`,
|
|
81
|
+
...externalMcpAllowedTools,
|
|
77
82
|
],
|
|
78
83
|
// SDK v0.2.133 deprecated passing "Skill" in allowedTools — the new path
|
|
79
84
|
// is the top-level `skills` option. The SDK appends `"Skill"` (or
|
|
@@ -81,7 +86,7 @@ export function sdkOptions(internalMcpServer, resumeSessionId, model, sessionCon
|
|
|
81
86
|
// `skills` is defined, so we set it to "all" to keep every discovered
|
|
82
87
|
// skill invocable (same surface as the old `"Skill"` allowedTools entry).
|
|
83
88
|
skills: "all",
|
|
84
|
-
mcpServers: { [TOMO_INTERNAL_MCP_NAME]: internalMcpServer },
|
|
89
|
+
mcpServers: { ...externalMcpServers, [TOMO_INTERNAL_MCP_NAME]: internalMcpServer },
|
|
85
90
|
settingSources: ["project"],
|
|
86
91
|
settings: {
|
|
87
92
|
attribution: {
|
|
@@ -92,6 +97,7 @@ export function sdkOptions(internalMcpServer, resumeSessionId, model, sessionCon
|
|
|
92
97
|
// See ./permissions.ts — canUseTool re-allows `.claude/skills/` writes
|
|
93
98
|
// that bypassPermissions otherwise routes here as denials.
|
|
94
99
|
canUseTool: skillsCanUseTool,
|
|
100
|
+
...(sessionContext?.onMcpElicitation ? { onElicitation: sessionContext.onMcpElicitation } : {}),
|
|
95
101
|
includePartialMessages: true,
|
|
96
102
|
maxTurns: config.maxTurns,
|
|
97
103
|
...buildHooksOption({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk-options.js","sourceRoot":"","sources":["../../src/agent/sdk-options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,2EAA2E;AAC3E,uEAAuE;AACvE,uEAAuE;AACvE,uEAAuE;AACvE,8EAA8E;AAC9E,+CAA+C;AAE/C;;6DAE6D;AAC7D,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,MAAM,CAAC,GAAG,CAAC,iBAAiB,KAAK,KAAK,CAAC;AAChD,CAAC;AAaD,MAAM,UAAU,cAAc;IAC5B,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAa;IAC3C,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACZ,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC;AACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk-options.js","sourceRoot":"","sources":["../../src/agent/sdk-options.ts"],"names":[],"mappings":"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"}
|
package/dist/agent.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare class Agent {
|
|
|
30
30
|
private latestInboundMessages;
|
|
31
31
|
private restoringConfig;
|
|
32
32
|
private readonly internalMcpServer;
|
|
33
|
+
private readonly mcpOAuthManager;
|
|
33
34
|
constructor();
|
|
34
35
|
/** Look up a channel by name */
|
|
35
36
|
private getChannel;
|
|
@@ -71,6 +72,11 @@ export declare class Agent {
|
|
|
71
72
|
private restoreConfigAndRestart;
|
|
72
73
|
private handleCommand;
|
|
73
74
|
private getOrCreateLiveSession;
|
|
75
|
+
private forwardMcpAuthorizeUrl;
|
|
76
|
+
private handleMcpAuthFailure;
|
|
77
|
+
private handleMcpElicitation;
|
|
78
|
+
private resolvePrivateReplyTarget;
|
|
79
|
+
private findPrivateReplyTarget;
|
|
74
80
|
private closeLiveSession;
|
|
75
81
|
private hashString;
|
|
76
82
|
/**
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAmB,eAAe,EAAoB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/agent.js
CHANGED
|
@@ -5,6 +5,7 @@ import { checkAndClearCompactTrigger } from "./lcm/index.js";
|
|
|
5
5
|
import { isGroupSessionKey } from "./lcm/blocks.js";
|
|
6
6
|
import { IdentityRouter } from "./router.js";
|
|
7
7
|
import { createTomoInternalMcpServer } from "./mcp/internal-server.js";
|
|
8
|
+
import { McpOAuthManager } from "./mcp/oauth.js";
|
|
8
9
|
import { log } from "./logger.js";
|
|
9
10
|
import { LiveSession } from "./agent/live-session.js";
|
|
10
11
|
import { makeTurnBudget, sdkOptions, usesLcmCompact } from "./agent/sdk-options.js";
|
|
@@ -36,10 +37,15 @@ export class Agent {
|
|
|
36
37
|
latestInboundMessages = new Map();
|
|
37
38
|
restoringConfig = false;
|
|
38
39
|
internalMcpServer;
|
|
40
|
+
mcpOAuthManager;
|
|
39
41
|
constructor() {
|
|
40
42
|
this.sessions = new SessionStore(config.sessionsDir, config.historyLimit);
|
|
41
43
|
this.router = new IdentityRouter(config.identities, this.sessions, config.channelAllowlists);
|
|
42
44
|
this.internalMcpServer = createTomoInternalMcpServer(this);
|
|
45
|
+
this.mcpOAuthManager = new McpOAuthManager({
|
|
46
|
+
workspaceDir: config.workspaceDir,
|
|
47
|
+
onServerAuthError: (serverName, err) => this.handleMcpAuthFailure(serverName, err),
|
|
48
|
+
});
|
|
43
49
|
// Load persistent per-session model overrides
|
|
44
50
|
for (const [key, model] of Object.entries(config.sessionModelOverrides)) {
|
|
45
51
|
this.modelOverrides.set(key, model);
|
|
@@ -166,7 +172,9 @@ export class Agent {
|
|
|
166
172
|
}
|
|
167
173
|
static AVAILABLE_MODELS = {
|
|
168
174
|
"sonnet": "claude-sonnet-4-6",
|
|
169
|
-
"
|
|
175
|
+
"sonnet-1m": "claude-sonnet-4-6[1m]",
|
|
176
|
+
"opus": "claude-opus-4-8",
|
|
177
|
+
"opus-1m": "claude-opus-4-8[1m]",
|
|
170
178
|
"haiku": "claude-haiku-4-5",
|
|
171
179
|
};
|
|
172
180
|
backupConfig() {
|
|
@@ -300,7 +308,7 @@ export class Agent {
|
|
|
300
308
|
return;
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
|
-
getOrCreateLiveSession(key) {
|
|
311
|
+
async getOrCreateLiveSession(key) {
|
|
304
312
|
let session = this.liveSessions.get(key);
|
|
305
313
|
if (session?.isAlive())
|
|
306
314
|
return session;
|
|
@@ -317,16 +325,108 @@ export class Agent {
|
|
|
317
325
|
const resumeId = this.sessions.getSdkSessionId(key);
|
|
318
326
|
const model = this.modelOverrides.get(key);
|
|
319
327
|
const turnBudget = makeTurnBudget();
|
|
328
|
+
const externalMcpServers = await this.mcpOAuthManager.buildServersWithAuth(config.mcpServers ?? {}, (serverName, url) => this.forwardMcpAuthorizeUrl(key, serverName, url));
|
|
320
329
|
const opts = sdkOptions(this.internalMcpServer, resumeId ?? undefined, model, {
|
|
321
330
|
sessionKey: key,
|
|
322
331
|
sdkSessionId: resumeId ?? undefined,
|
|
323
332
|
group: this.buildGroupContext(key),
|
|
324
|
-
|
|
333
|
+
onMcpElicitation: (request) => this.handleMcpElicitation(key, request),
|
|
334
|
+
}, turnBudget, externalMcpServers);
|
|
325
335
|
session = new LiveSession(opts, key, turnBudget);
|
|
326
336
|
this.liveSessions.set(key, session);
|
|
327
337
|
log.info({ key, resume: !!resumeId, model: opts.model }, "Live session created");
|
|
328
338
|
return session;
|
|
329
339
|
}
|
|
340
|
+
async forwardMcpAuthorizeUrl(key, serverName, url) {
|
|
341
|
+
const target = this.resolvePrivateReplyTarget(key);
|
|
342
|
+
if (!target)
|
|
343
|
+
throw new Error(`No private reply target is available for MCP OAuth login (${serverName})`);
|
|
344
|
+
const channel = this.getChannel(target.channelName);
|
|
345
|
+
if (!channel)
|
|
346
|
+
throw new Error(`Channel "${target.channelName}" is not connected for MCP OAuth login (${serverName})`);
|
|
347
|
+
await channel.send({
|
|
348
|
+
chatId: target.chatId,
|
|
349
|
+
text: [
|
|
350
|
+
`MCP login required for ${serverName}.`,
|
|
351
|
+
"",
|
|
352
|
+
url,
|
|
353
|
+
"",
|
|
354
|
+
"Open the link and finish login. Tomo will continue after the browser callback completes.",
|
|
355
|
+
].join("\n"),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
async handleMcpAuthFailure(serverName, err) {
|
|
359
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
360
|
+
log.warn({ serverName, err }, "External MCP server omitted after OAuth failure");
|
|
361
|
+
const target = this.findPrivateReplyTarget();
|
|
362
|
+
if (!target)
|
|
363
|
+
return;
|
|
364
|
+
const channel = this.getChannel(target.channelName);
|
|
365
|
+
if (!channel)
|
|
366
|
+
return;
|
|
367
|
+
await channel.send({
|
|
368
|
+
chatId: target.chatId,
|
|
369
|
+
text: `MCP server "${serverName}" is unavailable because OAuth failed or timed out: ${detail}. Continuing without that server.`,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
async handleMcpElicitation(key, request) {
|
|
373
|
+
const target = this.resolvePrivateReplyTarget(key);
|
|
374
|
+
if (!target) {
|
|
375
|
+
log.warn({ key, server: request.serverName }, "MCP elicitation requested but no private reply target is available");
|
|
376
|
+
return { action: "decline" };
|
|
377
|
+
}
|
|
378
|
+
const channel = this.getChannel(target.channelName);
|
|
379
|
+
if (!channel) {
|
|
380
|
+
log.warn({ key, channelName: target.channelName, server: request.serverName }, "MCP elicitation requested but channel is not connected");
|
|
381
|
+
return { action: "decline" };
|
|
382
|
+
}
|
|
383
|
+
if (request.mode === "url" && request.url) {
|
|
384
|
+
const lines = [
|
|
385
|
+
`MCP login required for ${request.serverName}.`,
|
|
386
|
+
request.message,
|
|
387
|
+
"",
|
|
388
|
+
request.url,
|
|
389
|
+
"",
|
|
390
|
+
"Open the link and finish login. If Tomo does not continue automatically, retry your request after login completes.",
|
|
391
|
+
];
|
|
392
|
+
await channel.send({ chatId: target.chatId, text: lines.filter(Boolean).join("\n") });
|
|
393
|
+
log.info({ key, server: request.serverName }, "Forwarded MCP login URL to user");
|
|
394
|
+
return { action: "accept" };
|
|
395
|
+
}
|
|
396
|
+
await channel.send({
|
|
397
|
+
chatId: target.chatId,
|
|
398
|
+
text: `MCP server ${request.serverName} requested additional input, but Tomo can only forward browser login links right now.`,
|
|
399
|
+
});
|
|
400
|
+
log.warn({ key, server: request.serverName, mode: request.mode }, "Declined unsupported MCP elicitation");
|
|
401
|
+
return { action: "decline" };
|
|
402
|
+
}
|
|
403
|
+
resolvePrivateReplyTarget(key) {
|
|
404
|
+
if (key.startsWith("dm:")) {
|
|
405
|
+
return this.router.getReplyTarget(key) ?? this.router.deriveReplyTargetFromConfig(key.slice(3));
|
|
406
|
+
}
|
|
407
|
+
if (!isGroupSessionKey(key)) {
|
|
408
|
+
return this.parseChannelKey(key);
|
|
409
|
+
}
|
|
410
|
+
for (const identity of config.identities) {
|
|
411
|
+
const target = this.router.deriveReplyTargetFromConfig(identity.name);
|
|
412
|
+
if (target)
|
|
413
|
+
return target;
|
|
414
|
+
}
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
findPrivateReplyTarget() {
|
|
418
|
+
for (const [key] of this.sessions.listSdkSessionIds()) {
|
|
419
|
+
const target = this.resolvePrivateReplyTarget(key);
|
|
420
|
+
if (target)
|
|
421
|
+
return target;
|
|
422
|
+
}
|
|
423
|
+
for (const identity of config.identities) {
|
|
424
|
+
const target = this.router.deriveReplyTargetFromConfig(identity.name);
|
|
425
|
+
if (target)
|
|
426
|
+
return target;
|
|
427
|
+
}
|
|
428
|
+
return undefined;
|
|
429
|
+
}
|
|
330
430
|
closeLiveSession(key) {
|
|
331
431
|
const session = this.liveSessions.get(key);
|
|
332
432
|
if (session) {
|
|
@@ -568,7 +668,7 @@ export class Agent {
|
|
|
568
668
|
}
|
|
569
669
|
async runWithRetry(key, prompt, onText, images, onBlockComplete, documents) {
|
|
570
670
|
try {
|
|
571
|
-
const session = this.getOrCreateLiveSession(key);
|
|
671
|
+
const session = await this.getOrCreateLiveSession(key);
|
|
572
672
|
const response = await session.send(prompt, onText, images, onBlockComplete, documents);
|
|
573
673
|
// Capture session ID if new
|
|
574
674
|
const sid = session.getSessionId();
|
|
@@ -627,7 +727,7 @@ export class Agent {
|
|
|
627
727
|
log.warn({ err }, "Session error, resetting and retrying");
|
|
628
728
|
this.closeLiveSession(key);
|
|
629
729
|
this.sessions.clearSdkSessionId(key);
|
|
630
|
-
const session = this.getOrCreateLiveSession(key);
|
|
730
|
+
const session = await this.getOrCreateLiveSession(key);
|
|
631
731
|
return session.send(prompt, onText, images, onBlockComplete, documents);
|
|
632
732
|
}
|
|
633
733
|
throw err;
|