@ziggs-ai/agent-sdk 0.1.11 → 0.2.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 +14 -0
- package/README.md +41 -2
- package/dist/AgentHost.d.ts +201 -0
- package/dist/AgentHost.js +652 -0
- package/dist/adapters/AnthropicAdapter.d.ts +39 -0
- package/dist/adapters/AnthropicAdapter.js +200 -0
- package/dist/adapters/McpAdapter.d.ts +199 -0
- package/dist/adapters/McpAdapter.js +468 -0
- package/dist/adapters/OpenAIAdapter.d.ts +26 -0
- package/dist/adapters/OpenAIAdapter.js +158 -0
- package/dist/adapters/index.js +2 -0
- package/dist/agent/Agent.d.ts +78 -0
- package/dist/agent/Agent.js +58 -0
- package/dist/agent/ClaudeSdkAgent.d.ts +38 -0
- package/dist/agent/ClaudeSdkAgent.js +322 -0
- package/dist/agent/claudeCognition.d.ts +67 -0
- package/dist/agent/claudeCognition.js +103 -0
- package/dist/agent/claudeSdkProtocol.d.ts +11 -0
- package/dist/agent/claudeSdkProtocol.js +27 -0
- package/dist/agent/claudeSdkTools.d.ts +19 -0
- package/dist/agent/claudeSdkTools.js +57 -0
- package/dist/agent/hostedAgentConnection.d.ts +15 -0
- package/dist/agent/hostedAgentConnection.js +29 -0
- package/dist/agent/toolManagerToSdkMcp.d.ts +12 -0
- package/dist/agent/toolManagerToSdkMcp.js +123 -0
- package/dist/brain/index.d.ts +50 -0
- package/dist/brain/index.js +45 -0
- package/dist/cognition/validateContext.d.ts +11 -0
- package/dist/cognition/validateContext.js +117 -0
- package/dist/context/applyEffects.d.ts +2 -0
- package/dist/context/applyEffects.js +89 -0
- package/dist/context/batch.d.ts +2 -0
- package/dist/context/batch.js +18 -0
- package/dist/context/classifyEnvelope.d.ts +1 -0
- package/dist/context/classifyEnvelope.js +58 -0
- package/dist/context/routingLabels.d.ts +4 -0
- package/dist/context/routingLabels.js +48 -0
- package/dist/defineAgent.d.ts +14 -0
- package/dist/defineAgent.js +68 -0
- package/dist/formatters/AgreementFormatter.d.ts +18 -0
- package/dist/formatters/AgreementFormatter.js +130 -0
- package/dist/formatters/HistoryFormatter.d.ts +26 -0
- package/dist/formatters/HistoryFormatter.js +173 -0
- package/dist/formatters/index.js +2 -0
- package/{src/index.ts → dist/index.d.ts} +13 -53
- package/dist/index.js +66 -0
- package/dist/ingress/normalizeIncoming.d.ts +65 -0
- package/dist/ingress/normalizeIncoming.js +88 -0
- package/dist/memory/MemoryStore.d.ts +55 -0
- package/dist/memory/MemoryStore.js +78 -0
- package/dist/pricing/fleetDefaults.d.ts +46 -0
- package/dist/pricing/fleetDefaults.js +168 -0
- package/dist/pricing/fleetEvalFree.d.ts +7 -0
- package/dist/pricing/fleetEvalFree.js +24 -0
- package/dist/pricing/fleetFreeTierA.gen.d.ts +6 -0
- package/{src/pricing/fleetFreeTierA.gen.ts → dist/pricing/fleetFreeTierA.gen.js} +5 -6
- package/dist/pricing/fleetTierByAgentId.gen.d.ts +7 -0
- package/dist/pricing/fleetTierByAgentId.gen.js +768 -0
- package/dist/runtime/AgentMachine.d.ts +143 -0
- package/dist/runtime/AgentMachine.js +400 -0
- package/dist/runtime/PromptBuilder.d.ts +96 -0
- package/dist/runtime/PromptBuilder.js +439 -0
- package/dist/runtime/bootstrapCtx.d.ts +18 -0
- package/dist/runtime/bootstrapCtx.js +37 -0
- package/dist/runtime/buildOutcome.d.ts +68 -0
- package/dist/runtime/buildOutcome.js +464 -0
- package/dist/runtime/defaults.d.ts +54 -0
- package/dist/runtime/defaults.js +47 -0
- package/dist/runtime/deriveRespondsToProposals.d.ts +9 -0
- package/dist/runtime/deriveRespondsToProposals.js +38 -0
- package/dist/runtime/kickoff.d.ts +15 -0
- package/dist/runtime/kickoff.js +112 -0
- package/dist/runtime/runMechanical.d.ts +25 -0
- package/dist/runtime/runMechanical.js +74 -0
- package/dist/runtime/runTurn.d.ts +63 -0
- package/dist/runtime/runTurn.js +800 -0
- package/dist/runtime/validateWorkflow.d.ts +17 -0
- package/dist/runtime/validateWorkflow.js +209 -0
- package/dist/server/ConnectionPool.d.ts +68 -0
- package/dist/server/ConnectionPool.js +166 -0
- package/dist/server/EventQueue.d.ts +33 -0
- package/dist/server/EventQueue.js +98 -0
- package/dist/server/InboxCatchUp.d.ts +24 -0
- package/dist/server/InboxCatchUp.js +213 -0
- package/dist/server/OutboxBuffer.d.ts +33 -0
- package/dist/server/OutboxBuffer.js +68 -0
- package/dist/server/SeenMessages.d.ts +13 -0
- package/dist/server/SeenMessages.js +31 -0
- package/dist/server/StateTimers.d.ts +34 -0
- package/dist/server/StateTimers.js +70 -0
- package/dist/server/ZiggsEffectHandler.d.ts +79 -0
- package/dist/server/ZiggsEffectHandler.js +319 -0
- package/dist/server/agreements/AgreementService.d.ts +50 -0
- package/dist/server/agreements/AgreementService.js +85 -0
- package/dist/server/createHealthServer.d.ts +7 -0
- package/dist/server/createHealthServer.js +75 -0
- package/dist/server/proactive/ProactiveTrigger.d.ts +33 -0
- package/dist/server/proactive/ProactiveTrigger.js +71 -0
- package/dist/server/runLauncher.d.ts +28 -0
- package/dist/server/runLauncher.js +93 -0
- package/dist/server/tasks/TaskService.d.ts +25 -0
- package/dist/server/tasks/TaskService.js +103 -0
- package/dist/server/tasks/index.js +1 -0
- package/dist/server/telemetryIngest.d.ts +24 -0
- package/dist/server/telemetryIngest.js +62 -0
- package/dist/server/tools/index.d.ts +24 -0
- package/{src/server/tools/index.ts → dist/server/tools/index.js} +4 -24
- package/dist/server/tools/tier1/protocolRunner.d.ts +39 -0
- package/dist/server/tools/tier1/protocolRunner.js +288 -0
- package/dist/server/tools/tier1/protocolTools.d.ts +12 -0
- package/dist/server/tools/tier1/protocolTools.js +37 -0
- package/dist/server/tools/tier2/connectionTools.d.ts +4 -0
- package/dist/server/tools/tier2/connectionTools.js +63 -0
- package/dist/server/tools/tier2/contextTools.d.ts +4 -0
- package/dist/server/tools/tier2/contextTools.js +46 -0
- package/dist/server/tools/tier2/discoveryTools.d.ts +4 -0
- package/dist/server/tools/tier2/discoveryTools.js +25 -0
- package/dist/server/tools/tier2/marketplaceTools.d.ts +3 -0
- package/dist/server/tools/tier2/marketplaceTools.js +22 -0
- package/dist/server/tools/tier2/paymentTools.d.ts +12 -0
- package/dist/server/tools/tier2/paymentTools.js +165 -0
- package/dist/server/ziggsconnect/ZiggsConnectClient.d.ts +35 -0
- package/dist/server/ziggsconnect/ZiggsConnectClient.js +95 -0
- package/dist/server/ziggscontext/ZiggsContextClient.d.ts +48 -0
- package/dist/server/ziggscontext/ZiggsContextClient.js +90 -0
- package/dist/server/ziggspay/ZiggsPayClient.d.ts +82 -0
- package/dist/server/ziggspay/ZiggsPayClient.js +188 -0
- package/dist/shared/ids.d.ts +1 -0
- package/dist/shared/ids.js +3 -0
- package/dist/shared/runtimeLog.d.ts +18 -0
- package/dist/shared/runtimeLog.js +68 -0
- package/dist/shared/types.d.ts +22 -0
- package/dist/shared/types.js +23 -0
- package/dist/tasks/protocolRegistry.d.ts +16 -0
- package/dist/tasks/protocolRegistry.js +20 -0
- package/dist/tools/ToolManager.d.ts +12 -0
- package/dist/tools/ToolManager.js +74 -0
- package/dist/tools/ToolProvider.d.ts +6 -0
- package/dist/tools/ToolProvider.js +21 -0
- package/dist/tools/defineTool.d.ts +27 -0
- package/dist/tools/defineTool.js +62 -0
- package/{src/tools/index.ts → dist/tools/index.d.ts} +0 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/recordReport.d.ts +41 -0
- package/dist/tools/recordReport.js +30 -0
- package/dist/types.d.ts +466 -0
- package/dist/types.js +7 -0
- package/dist/utils/jsonExtractor.d.ts +2 -0
- package/dist/utils/jsonExtractor.js +111 -0
- package/package.json +17 -7
- package/src/AgentHost.ts +0 -545
- package/src/adapters/AnthropicAdapter.ts +0 -206
- package/src/adapters/McpAdapter.ts +0 -348
- package/src/adapters/OpenAIAdapter.ts +0 -187
- package/src/agent/Agent.ts +0 -104
- package/src/cognition/validateContext.ts +0 -95
- package/src/context/applyEffects.ts +0 -80
- package/src/context/batch.ts +0 -19
- package/src/context/classifyEnvelope.ts +0 -38
- package/src/context/routingLabels.ts +0 -46
- package/src/defineAgent.ts +0 -70
- package/src/formatters/AgreementFormatter.ts +0 -111
- package/src/formatters/HistoryFormatter.ts +0 -172
- package/src/ingress/normalizeIncoming.ts +0 -162
- package/src/memory/MemoryStore.ts +0 -104
- package/src/pricing/fleetDefaults.ts +0 -225
- package/src/pricing/fleetEvalFree.ts +0 -24
- package/src/pricing/fleetTierByAgentId.gen.ts +0 -775
- package/src/runtime/AgentMachine.ts +0 -402
- package/src/runtime/PromptBuilder.ts +0 -485
- package/src/runtime/buildOutcome.ts +0 -563
- package/src/runtime/defaults.ts +0 -75
- package/src/runtime/kickoff.ts +0 -117
- package/src/runtime/runTurn.ts +0 -813
- package/src/runtime/validateWorkflow.ts +0 -202
- package/src/server/ConnectionPool.ts +0 -206
- package/src/server/EventQueue.ts +0 -133
- package/src/server/InboxCatchUp.ts +0 -254
- package/src/server/OutboxBuffer.ts +0 -90
- package/src/server/SeenMessages.ts +0 -27
- package/src/server/StateTimers.ts +0 -85
- package/src/server/ZiggsEffectHandler.ts +0 -418
- package/src/server/agreements/AgreementService.ts +0 -118
- package/src/server/createHealthServer.ts +0 -85
- package/src/server/proactive/ProactiveTrigger.ts +0 -83
- package/src/server/runLauncher.ts +0 -146
- package/src/server/tasks/TaskService.ts +0 -110
- package/src/server/telemetryIngest.ts +0 -91
- package/src/server/tools/tier1/protocolRunner.ts +0 -144
- package/src/server/tools/tier1/protocolTools.ts +0 -99
- package/src/server/tools/tier2/connectionTools.ts +0 -75
- package/src/server/tools/tier2/contextTools.ts +0 -63
- package/src/server/tools/tier2/discoveryTools.ts +0 -34
- package/src/server/tools/tier2/marketplaceTools.ts +0 -27
- package/src/server/tools/tier2/paymentTools.ts +0 -170
- package/src/server/ziggsconnect/ZiggsConnectClient.ts +0 -126
- package/src/server/ziggscontext/ZiggsContextClient.ts +0 -129
- package/src/server/ziggspay/ZiggsPayClient.ts +0 -193
- package/src/shared/ids.ts +0 -3
- package/src/shared/runtimeLog.ts +0 -72
- package/src/shared/types.ts +0 -29
- package/src/tasks/protocolRegistry.ts +0 -25
- package/src/tools/ToolManager.ts +0 -95
- package/src/tools/ToolProvider.ts +0 -19
- package/src/tools/defineTool.ts +0 -90
- package/src/tools/recordReport.ts +0 -82
- package/src/types.ts +0 -456
- package/src/utils/jsonExtractor.ts +0 -100
- /package/{src/adapters/index.ts → dist/adapters/index.d.ts} +0 -0
- /package/{src/formatters/index.ts → dist/formatters/index.d.ts} +0 -0
- /package/{src/server/tasks/index.ts → dist/server/tasks/index.d.ts} +0 -0
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import type { LlmMessage, LlmResponse, LlmToolCall, LlmToolSchema } from '../types.js';
|
|
3
|
-
import { runtimeLog } from '../shared/runtimeLog.js';
|
|
4
|
-
|
|
5
|
-
interface AdapterOptions {
|
|
6
|
-
key?: string;
|
|
7
|
-
model?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Claude 4.7+ models reject sampling params (temperature/top_p/top_k) with a
|
|
11
|
-
// 400 and run without thinking when the `thinking` param is omitted. We omit
|
|
12
|
-
// both on purpose: the SDK's LlmMessage history cannot round-trip thinking
|
|
13
|
-
// blocks through the tool loop, so thinking must stay off.
|
|
14
|
-
const MAX_COMPLETION_TOKENS = 16000;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Anthropic adapter satisfying the same LlmAdapter contract as OpenAIAdapter:
|
|
18
|
-
* OpenAI-wire-shaped LlmMessage[] in, { content, tool_calls, usage } out.
|
|
19
|
-
* Selected by AgentHost when the configured model id starts with "claude".
|
|
20
|
-
*/
|
|
21
|
-
export class AnthropicAdapter {
|
|
22
|
-
private client: Anthropic;
|
|
23
|
-
model: string;
|
|
24
|
-
|
|
25
|
-
constructor({ key, model = 'claude-opus-4-8' }: AdapterOptions) {
|
|
26
|
-
this.client = new Anthropic({ apiKey: key });
|
|
27
|
-
this.model = model;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async chat(
|
|
31
|
-
prompt: string,
|
|
32
|
-
tools: LlmToolSchema[] = [],
|
|
33
|
-
_temperature = 0,
|
|
34
|
-
systemHints: LlmMessage[] = [],
|
|
35
|
-
options: Record<string, unknown> = {},
|
|
36
|
-
): Promise<LlmResponse> {
|
|
37
|
-
const messages: LlmMessage[] = [
|
|
38
|
-
{ role: 'system', content: 'Respond with valid JSON.' },
|
|
39
|
-
...systemHints,
|
|
40
|
-
{ role: 'user', content: prompt },
|
|
41
|
-
];
|
|
42
|
-
return this.chatMessages(messages, tools, options);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async chatMessages(
|
|
46
|
-
messages: LlmMessage[],
|
|
47
|
-
tools: LlmToolSchema[] = [],
|
|
48
|
-
_options: Record<string, unknown> = {},
|
|
49
|
-
): Promise<LlmResponse> {
|
|
50
|
-
const { system, anthropicMessages } = translateMessages(messages);
|
|
51
|
-
const anthropicTools: Anthropic.Tool[] | undefined = tools.length > 0
|
|
52
|
-
? tools.map(t => ({
|
|
53
|
-
name: t.function.name,
|
|
54
|
-
description: t.function.description ?? '',
|
|
55
|
-
input_schema: (t.function.parameters ?? { type: 'object' }) as Anthropic.Tool.InputSchema,
|
|
56
|
-
}))
|
|
57
|
-
: undefined;
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const res = await this.client.messages.create({
|
|
61
|
-
model: this.model,
|
|
62
|
-
max_tokens: MAX_COMPLETION_TOKENS,
|
|
63
|
-
...(system ? { system } : {}),
|
|
64
|
-
messages: anthropicMessages,
|
|
65
|
-
...(anthropicTools ? { tools: anthropicTools } : {}),
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
if (res.stop_reason === 'refusal') {
|
|
69
|
-
throw new Error(`LLM refused the request (stop_reason=refusal)`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const content = res.content
|
|
73
|
-
.filter((b): b is Anthropic.TextBlock => b.type === 'text')
|
|
74
|
-
.map(b => b.text)
|
|
75
|
-
.join('')
|
|
76
|
-
.trim() || null;
|
|
77
|
-
const toolCalls: LlmToolCall[] | null = (() => {
|
|
78
|
-
const uses = res.content.filter((b): b is Anthropic.ToolUseBlock => b.type === 'tool_use');
|
|
79
|
-
if (!uses.length) return null;
|
|
80
|
-
return uses.map(u => ({
|
|
81
|
-
id: u.id,
|
|
82
|
-
function: { name: u.name, arguments: JSON.stringify(u.input ?? {}) },
|
|
83
|
-
}));
|
|
84
|
-
})();
|
|
85
|
-
|
|
86
|
-
if (!content && !toolCalls && res.stop_reason === 'max_tokens') {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`LLM returned no usable output — completion token budget exhausted before any content (stop_reason=max_tokens)`,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
content: content ?? undefined,
|
|
94
|
-
tool_calls: toolCalls ?? undefined,
|
|
95
|
-
usage: res.usage
|
|
96
|
-
? {
|
|
97
|
-
prompt_tokens: res.usage.input_tokens,
|
|
98
|
-
completion_tokens: res.usage.output_tokens,
|
|
99
|
-
total_tokens: res.usage.input_tokens + res.usage.output_tokens,
|
|
100
|
-
}
|
|
101
|
-
: undefined,
|
|
102
|
-
};
|
|
103
|
-
} catch (error) {
|
|
104
|
-
const err = error as Error & { status?: number };
|
|
105
|
-
runtimeLog.error('AnthropicAdapter', `LLM API call failed: ${err.message}`, {
|
|
106
|
-
model: this.model,
|
|
107
|
-
error: err.message,
|
|
108
|
-
status: err.status ?? 'unknown',
|
|
109
|
-
});
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async chatMessagesStream(
|
|
115
|
-
messages: LlmMessage[],
|
|
116
|
-
onChunk: (text: string) => void,
|
|
117
|
-
): Promise<string> {
|
|
118
|
-
const { system, anthropicMessages } = translateMessages(messages);
|
|
119
|
-
const stream = this.client.messages.stream({
|
|
120
|
-
model: this.model,
|
|
121
|
-
max_tokens: MAX_COMPLETION_TOKENS,
|
|
122
|
-
...(system ? { system } : {}),
|
|
123
|
-
messages: anthropicMessages,
|
|
124
|
-
});
|
|
125
|
-
let text = '';
|
|
126
|
-
stream.on('text', (delta) => {
|
|
127
|
-
text += delta;
|
|
128
|
-
onChunk(delta);
|
|
129
|
-
});
|
|
130
|
-
await stream.finalMessage();
|
|
131
|
-
return text;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* OpenAI-wire-shaped LlmMessage[] → Anthropic { system, messages }.
|
|
137
|
-
* - system messages concatenate into the top-level system param
|
|
138
|
-
* - assistant tool_calls become tool_use blocks (arguments JSON-parsed)
|
|
139
|
-
* - role:"tool" results become user-turn tool_result blocks
|
|
140
|
-
* Consecutive same-role turns are merged (Anthropic requires alternation to
|
|
141
|
-
* start with user; the API tolerates consecutive same-role, but tool_result
|
|
142
|
-
* blocks must open the user turn that answers a tool_use turn).
|
|
143
|
-
*/
|
|
144
|
-
function translateMessages(messages: LlmMessage[]): {
|
|
145
|
-
system: string | undefined;
|
|
146
|
-
anthropicMessages: Anthropic.MessageParam[];
|
|
147
|
-
} {
|
|
148
|
-
const systemParts: string[] = [];
|
|
149
|
-
const out: Anthropic.MessageParam[] = [];
|
|
150
|
-
|
|
151
|
-
for (const m of messages) {
|
|
152
|
-
if (m.role === 'system') {
|
|
153
|
-
if (m.content) systemParts.push(m.content);
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
if (m.role === 'tool') {
|
|
157
|
-
const block: Anthropic.ToolResultBlockParam = {
|
|
158
|
-
type: 'tool_result',
|
|
159
|
-
tool_use_id: m.tool_call_id ?? '',
|
|
160
|
-
content: m.content ?? '',
|
|
161
|
-
};
|
|
162
|
-
const last = out[out.length - 1];
|
|
163
|
-
if (last && last.role === 'user' && Array.isArray(last.content)) {
|
|
164
|
-
(last.content as Anthropic.ContentBlockParam[]).push(block);
|
|
165
|
-
} else {
|
|
166
|
-
out.push({ role: 'user', content: [block] });
|
|
167
|
-
}
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (m.role === 'assistant' && m.tool_calls?.length) {
|
|
171
|
-
const blocks: Anthropic.ContentBlockParam[] = [];
|
|
172
|
-
if (m.content) blocks.push({ type: 'text', text: m.content });
|
|
173
|
-
for (const tc of m.tool_calls) {
|
|
174
|
-
blocks.push({
|
|
175
|
-
type: 'tool_use',
|
|
176
|
-
id: tc.id,
|
|
177
|
-
name: tc.function.name,
|
|
178
|
-
input: safeParseJson(tc.function.arguments),
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
out.push({ role: 'assistant', content: blocks });
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
out.push({ role: m.role, content: m.content ?? '' });
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (!out.length || out[0].role !== 'user') {
|
|
188
|
-
out.unshift({ role: 'user', content: '(session start)' });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
system: systemParts.length ? systemParts.join('\n\n') : undefined,
|
|
193
|
-
anthropicMessages: out,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function safeParseJson(s: string): Record<string, unknown> {
|
|
198
|
-
try {
|
|
199
|
-
const v = JSON.parse(s);
|
|
200
|
-
return typeof v === 'object' && v !== null ? v : {};
|
|
201
|
-
} catch {
|
|
202
|
-
// Malformed arguments string from a prior turn's history: replaying it as
|
|
203
|
-
// an empty input beats crashing the whole turn on someone else's output.
|
|
204
|
-
return {};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
-
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
4
|
-
import { AgentHost, type AgentHostOptions } from '../AgentHost.js';
|
|
5
|
-
import type { ToolDefinition } from '../tools/defineTool.js';
|
|
6
|
-
import { thinkingDefaults } from '../runtime/defaults.js';
|
|
7
|
-
import type { Workflow } from '../types.js';
|
|
8
|
-
|
|
9
|
-
export type McpTransportConfig =
|
|
10
|
-
| { type: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }
|
|
11
|
-
| {
|
|
12
|
-
type: 'http';
|
|
13
|
-
url: string;
|
|
14
|
-
/**
|
|
15
|
-
* Optional static request headers — e.g. a fixed API key the server is
|
|
16
|
-
* keyed by. Sent on every request to the server. Values are used as-is;
|
|
17
|
-
* `$VAR` interpolation (if any) is the caller's job, mirroring how the
|
|
18
|
-
* stdio path leaves `env` interpolation to the caller.
|
|
19
|
-
*/
|
|
20
|
-
headers?: Record<string, string>;
|
|
21
|
-
}
|
|
22
|
-
| {
|
|
23
|
-
/**
|
|
24
|
-
* Brokered connection through the Ziggs MCP gateway: the agent speaks
|
|
25
|
-
* StreamableHTTP to `/connections/:id/mcp` and Ziggs injects the
|
|
26
|
-
* connection owner's OAuth token upstream. The agent never holds the
|
|
27
|
-
* token; every tools/call is checked against the grant's caveats.
|
|
28
|
-
*/
|
|
29
|
-
type: 'ziggs';
|
|
30
|
-
/** Connection id (`con_…`) of the user's MCP server connection. */
|
|
31
|
-
connectionId: string;
|
|
32
|
-
/** ConnectionGrant id (`cgr_…`) issued to this agent. */
|
|
33
|
-
grantId: string;
|
|
34
|
-
/** Operator key — defaults to ZIGGS_OPERATOR_KEY. */
|
|
35
|
-
operatorKey?: string;
|
|
36
|
-
/** Ziggs backend base URL — defaults to BACKEND_URL / ZIGGS_BACKEND_URL. */
|
|
37
|
-
baseUrl?: string;
|
|
38
|
-
/** Agent to impersonate (X-Agent-Id) — defaults to the host agentId. */
|
|
39
|
-
agentId?: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Typed failure from the Ziggs MCP gateway (ZIG-702). `code` is the stable
|
|
44
|
-
* machine-readable reason — e.g. `GRANT_REVOKED`, `GRANT_EXPIRED`,
|
|
45
|
-
* `GRANT_HOLDER_MISMATCH`, `GRANT_CONNECTION_MISMATCH` — so agent code can
|
|
46
|
-
* branch ("revoked → tell the user and stop retrying" vs "network blip →
|
|
47
|
-
* retry") instead of parsing transport-error prose.
|
|
48
|
-
*/
|
|
49
|
-
export class ZiggsGatewayError extends Error {
|
|
50
|
-
readonly code: string;
|
|
51
|
-
readonly status: number;
|
|
52
|
-
|
|
53
|
-
constructor(code: string, message: string, status: number) {
|
|
54
|
-
super(message);
|
|
55
|
-
this.name = 'ZiggsGatewayError';
|
|
56
|
-
this.code = code;
|
|
57
|
-
this.status = status;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Wrap fetch so structured gateway denials (401/403 with a `code` body)
|
|
63
|
-
* surface as ZiggsGatewayError instead of a generic transport failure.
|
|
64
|
-
*/
|
|
65
|
-
export function wrapGatewayFetch(
|
|
66
|
-
baseFetch: typeof fetch = fetch,
|
|
67
|
-
): typeof fetch {
|
|
68
|
-
return (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
69
|
-
const response = await baseFetch(input, init);
|
|
70
|
-
if (response.status === 401 || response.status === 403) {
|
|
71
|
-
const body = (await response
|
|
72
|
-
.clone()
|
|
73
|
-
.json()
|
|
74
|
-
.catch(() => null)) as { code?: unknown; message?: unknown } | null;
|
|
75
|
-
if (body && typeof body.code === 'string') {
|
|
76
|
-
throw new ZiggsGatewayError(
|
|
77
|
-
body.code,
|
|
78
|
-
typeof body.message === 'string' ? body.message : body.code,
|
|
79
|
-
response.status,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return response;
|
|
84
|
-
}) as typeof fetch;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Resolve the gateway URL + auth headers for a `type: 'ziggs'` transport. */
|
|
88
|
-
export function resolveZiggsGatewayTarget(
|
|
89
|
-
config: Extract<McpTransportConfig, { type: 'ziggs' }>,
|
|
90
|
-
defaults: { agentId?: string } = {},
|
|
91
|
-
): { url: URL; headers: Record<string, string> } {
|
|
92
|
-
if (!config.connectionId) {
|
|
93
|
-
throw new Error('ziggs MCP transport requires a connectionId');
|
|
94
|
-
}
|
|
95
|
-
if (!config.grantId) {
|
|
96
|
-
throw new Error('ziggs MCP transport requires a grantId');
|
|
97
|
-
}
|
|
98
|
-
const operatorKey = config.operatorKey || process.env.ZIGGS_OPERATOR_KEY;
|
|
99
|
-
if (!operatorKey) {
|
|
100
|
-
throw new Error(
|
|
101
|
-
'ziggs MCP transport requires an operatorKey (or ZIGGS_OPERATOR_KEY)',
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
const agentId = config.agentId || defaults.agentId;
|
|
105
|
-
if (!agentId) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
'ziggs MCP transport requires an agentId to impersonate (X-Agent-Id)',
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
const baseUrl = (
|
|
111
|
-
config.baseUrl ||
|
|
112
|
-
process.env.BACKEND_URL ||
|
|
113
|
-
process.env.ZIGGS_BACKEND_URL ||
|
|
114
|
-
'http://localhost:3000'
|
|
115
|
-
).replace(/\/$/, '');
|
|
116
|
-
return {
|
|
117
|
-
url: new URL(`${baseUrl}/connections/${encodeURIComponent(config.connectionId)}/mcp`),
|
|
118
|
-
headers: {
|
|
119
|
-
authorization: `Bearer ${operatorKey}`,
|
|
120
|
-
'x-agent-id': agentId,
|
|
121
|
-
'x-grant-id': config.grantId,
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface CreateMcpAgentOptions extends Omit<AgentHostOptions, 'tools' | 'workflow' | 'states' | 'initial'> {
|
|
127
|
-
/** MCP server connection — stdio (local process) or HTTP (remote). */
|
|
128
|
-
transport: McpTransportConfig;
|
|
129
|
-
/** Additional Ziggs tools to register alongside the MCP-derived ones. */
|
|
130
|
-
extraTools?: ToolDefinition[];
|
|
131
|
-
/** Override the default single-state thinking workflow. */
|
|
132
|
-
workflow?: Workflow;
|
|
133
|
-
/**
|
|
134
|
-
* Max tools to bind to the LLM per turn. LLM providers cap the `tools` array
|
|
135
|
-
* (OpenAI: 128); an MCP server exposing more (e.g. PostHog: 146) makes every
|
|
136
|
-
* turn fail with `400 ... array too long`. When the server's tool count
|
|
137
|
-
* exceeds this budget the list is truncated (and logged). Defaults to 128.
|
|
138
|
-
* See ZIG-759; Phase 2 will replace truncation with query-aware selection.
|
|
139
|
-
*/
|
|
140
|
-
maxTools?: number;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** Provider `tools` array cap (OpenAI's is 128). */
|
|
144
|
-
const DEFAULT_MAX_TOOLS = 128;
|
|
145
|
-
/** Headroom reserved for control actions (wait/respond) + future additions. */
|
|
146
|
-
const CONTROL_TOOL_RESERVE = 4;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* How many MCP tools may be bound to the LLM, given the provider cap and the
|
|
150
|
-
* number of extra (non-MCP) tools the agent also carries. Always >= 1 so a
|
|
151
|
-
* pathological config still binds something rather than nothing. ZIG-759.
|
|
152
|
-
*/
|
|
153
|
-
export function mcpToolBudget(maxTools: number, extraToolCount: number): number {
|
|
154
|
-
return Math.max(1, maxTools - CONTROL_TOOL_RESERVE - extraToolCount);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface McpAgentHandle {
|
|
158
|
-
host: AgentHost;
|
|
159
|
-
/** Disconnect the MCP client (call on shutdown alongside host.disconnect()). */
|
|
160
|
-
closeMcp: () => Promise<void>;
|
|
161
|
-
/** Names of MCP tools that were mapped to Ziggs tools on startup. */
|
|
162
|
-
toolNames: string[];
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function buildTransport(config: McpTransportConfig, defaults: { agentId?: string } = {}) {
|
|
166
|
-
if (config.type === 'stdio') {
|
|
167
|
-
return new StdioClientTransport({
|
|
168
|
-
command: config.command,
|
|
169
|
-
args: config.args ?? [],
|
|
170
|
-
env: config.env,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
if (config.type === 'ziggs') {
|
|
174
|
-
const target = resolveZiggsGatewayTarget(config, defaults);
|
|
175
|
-
return new StreamableHTTPClientTransport(target.url, {
|
|
176
|
-
requestInit: { headers: target.headers },
|
|
177
|
-
fetch: wrapGatewayFetch(),
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return new StreamableHTTPClientTransport(
|
|
181
|
-
new URL(config.url),
|
|
182
|
-
config.headers && Object.keys(config.headers).length > 0
|
|
183
|
-
? { requestInit: { headers: config.headers } }
|
|
184
|
-
: undefined,
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
type McpTool = {
|
|
189
|
-
name: string;
|
|
190
|
-
description?: string;
|
|
191
|
-
inputSchema?: Record<string, unknown>;
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
type McpContent = { type: string; text?: string };
|
|
195
|
-
|
|
196
|
-
function mcpContentToResult(content: McpContent[]): unknown {
|
|
197
|
-
const text = content
|
|
198
|
-
.map(c => (c.type === 'text' ? c.text ?? '' : JSON.stringify(c)))
|
|
199
|
-
.join('\n');
|
|
200
|
-
try {
|
|
201
|
-
return JSON.parse(text);
|
|
202
|
-
} catch {
|
|
203
|
-
return text;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function mapMcpTools(client: Client, tools: McpTool[]): ToolDefinition[] {
|
|
208
|
-
return tools.map(tool => ({
|
|
209
|
-
schema: {
|
|
210
|
-
type: 'function' as const,
|
|
211
|
-
function: {
|
|
212
|
-
name: tool.name,
|
|
213
|
-
description: tool.description ?? '',
|
|
214
|
-
parameters: tool.inputSchema ?? { type: 'object', properties: {} },
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
handler: async (args: Record<string, unknown>) => {
|
|
218
|
-
const result = await client.callTool({ name: tool.name, arguments: args }) as {
|
|
219
|
-
isError?: boolean;
|
|
220
|
-
content: McpContent[];
|
|
221
|
-
};
|
|
222
|
-
if (result.isError) {
|
|
223
|
-
throw new Error(mcpContentToResult(result.content) as string);
|
|
224
|
-
}
|
|
225
|
-
return mcpContentToResult(result.content);
|
|
226
|
-
},
|
|
227
|
-
isGenericFallback: true,
|
|
228
|
-
}));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function buildDefaultWorkflow(description: string, mcpTools: McpTool[]): Workflow {
|
|
232
|
-
const dflt = thinkingDefaults({ initial: 'idle' });
|
|
233
|
-
// Bind each MCP tool as an explicit action so runTurn includes them in the
|
|
234
|
-
// LLM API call and the decision schema. Without this, allowedToolNames is
|
|
235
|
-
// empty and the tools array passed to the LLM is also empty.
|
|
236
|
-
const toolActions = Object.fromEntries(
|
|
237
|
-
mcpTools.map(t => [t.name, {
|
|
238
|
-
tool: t.name,
|
|
239
|
-
prompt: { when: t.description || `Use ${t.name}` },
|
|
240
|
-
}]),
|
|
241
|
-
);
|
|
242
|
-
return {
|
|
243
|
-
id: 'mcp-agent',
|
|
244
|
-
description: description || 'MCP-backed agent',
|
|
245
|
-
initial: 'idle',
|
|
246
|
-
states: {
|
|
247
|
-
idle: {
|
|
248
|
-
kind: 'thinking',
|
|
249
|
-
prompt: {
|
|
250
|
-
role: 'You are a helpful agent powered by an MCP server.',
|
|
251
|
-
goal: description || 'Help the user by calling the available tools.',
|
|
252
|
-
constraints: 'Use your tools to answer the user\'s request. After gathering results, use "respond" to send the answer. Use "wait" only when no action is needed.',
|
|
253
|
-
},
|
|
254
|
-
actions: {
|
|
255
|
-
...dflt.actions,
|
|
256
|
-
...toolActions,
|
|
257
|
-
respond: {
|
|
258
|
-
prompt: {
|
|
259
|
-
instruction: 'Send your final answer to the user.',
|
|
260
|
-
when: 'You have gathered enough information and are ready to reply to the user.',
|
|
261
|
-
format: 'args: { receiverId: "<userId>", message: "<your answer>" }',
|
|
262
|
-
},
|
|
263
|
-
produces: 'message-sent',
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
transitions: [
|
|
267
|
-
{ to: 'idle', when: (o) => o.kind === 'wait' },
|
|
268
|
-
{ to: 'idle', when: (o) => o.kind === 'error' },
|
|
269
|
-
{ to: 'done', when: (o) => o.kind === 'message-sent' },
|
|
270
|
-
],
|
|
271
|
-
},
|
|
272
|
-
done: {
|
|
273
|
-
kind: 'parked',
|
|
274
|
-
transitions: [
|
|
275
|
-
{ to: 'idle', when: (o) => o.kind === 'message-received' },
|
|
276
|
-
],
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Wraps any MCP server as a Ziggs AgentHost.
|
|
284
|
-
*
|
|
285
|
-
* 1. Opens a connection to the MCP server (stdio, HTTP, or the Ziggs gateway).
|
|
286
|
-
* 2. Calls tools/list and maps each tool to a Ziggs ToolDefinition.
|
|
287
|
-
* 3. Returns an AgentHost pre-loaded with those tools, ready to connectAsync().
|
|
288
|
-
*
|
|
289
|
-
* Usage (local stdio server):
|
|
290
|
-
* const { host, closeMcp } = await createMcpAgent({
|
|
291
|
-
* transport: { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-github'] },
|
|
292
|
-
* agentId: 'my-github-agent',
|
|
293
|
-
* name: 'GitHub Agent',
|
|
294
|
-
* description: 'Wraps the GitHub MCP server',
|
|
295
|
-
* });
|
|
296
|
-
* await host.connectAsync();
|
|
297
|
-
*
|
|
298
|
-
* Usage (OAuth-protected remote server, brokered through Ziggs — the user
|
|
299
|
-
* connected the server and issued this agent a grant; the agent never sees
|
|
300
|
-
* the user's token):
|
|
301
|
-
* const { host, closeMcp } = await createMcpAgent({
|
|
302
|
-
* transport: { type: 'ziggs', connectionId: 'con_abc', grantId: 'cgr_xyz' },
|
|
303
|
-
* agentId: 'my-notion-agent',
|
|
304
|
-
* name: 'Notion Agent',
|
|
305
|
-
* description: 'Works in the owner\'s Notion via their MCP connection',
|
|
306
|
-
* });
|
|
307
|
-
* await host.connectAsync();
|
|
308
|
-
*
|
|
309
|
-
* process.on('SIGTERM', async () => {
|
|
310
|
-
* host.disconnect();
|
|
311
|
-
* await host.drain();
|
|
312
|
-
* await closeMcp();
|
|
313
|
-
* });
|
|
314
|
-
*/
|
|
315
|
-
export async function createMcpAgent(options: CreateMcpAgentOptions): Promise<McpAgentHandle> {
|
|
316
|
-
const { transport: transportConfig, extraTools = [], workflow, maxTools = DEFAULT_MAX_TOOLS, ...hostOptions } = options;
|
|
317
|
-
|
|
318
|
-
const transport = buildTransport(transportConfig, { agentId: hostOptions.agentId });
|
|
319
|
-
const client = new Client({ name: 'ziggs-mcp-agent', version: '1.0.0' });
|
|
320
|
-
|
|
321
|
-
await client.connect(transport);
|
|
322
|
-
|
|
323
|
-
const listed = (await client.listTools()).tools as McpTool[];
|
|
324
|
-
// Cap the tool set bound to the LLM: providers reject a `tools` array longer
|
|
325
|
-
// than their limit (OpenAI 128). Truncate — never crash — and log what dropped
|
|
326
|
-
// so a large server (e.g. PostHog: 146 tools) still answers. ZIG-759 Phase 1.
|
|
327
|
-
const budget = mcpToolBudget(maxTools, extraTools.length);
|
|
328
|
-
let rawTools = listed;
|
|
329
|
-
if (listed.length > budget) {
|
|
330
|
-
rawTools = listed.slice(0, budget);
|
|
331
|
-
console.warn(`[McpAdapter] ${hostOptions.agentId}: MCP server exposes ${listed.length} tools, over the ${maxTools}-tool LLM cap; binding the first ${rawTools.length}, dropping ${listed.length - rawTools.length}. (ZIG-759 Phase 2 will select per query.)`);
|
|
332
|
-
}
|
|
333
|
-
const mcpTools = mapMcpTools(client, rawTools);
|
|
334
|
-
|
|
335
|
-
const resolvedWorkflow = workflow ?? buildDefaultWorkflow(hostOptions.description ?? '', rawTools);
|
|
336
|
-
|
|
337
|
-
const host = new AgentHost({
|
|
338
|
-
...hostOptions,
|
|
339
|
-
workflow: resolvedWorkflow,
|
|
340
|
-
tools: [...mcpTools, ...extraTools],
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
return {
|
|
344
|
-
host,
|
|
345
|
-
closeMcp: () => client.close(),
|
|
346
|
-
toolNames: rawTools.map((t: McpTool) => t.name),
|
|
347
|
-
};
|
|
348
|
-
}
|