indusagi 0.13.7 → 0.13.8
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/dist/agent.js +28713 -0
- package/dist/ai.js +19964 -0
- package/dist/capabilities.js +2730 -0
- package/dist/cli.js +9 -0
- package/dist/connectors-saas.js +3355 -0
- package/dist/index.js +18211 -0
- package/dist/interop.js +3247 -0
- package/dist/llmgateway.js +3343 -0
- package/dist/mcp.js +2448 -0
- package/dist/memory.js +0 -0
- package/dist/react-host/index.js +101 -0
- package/dist/react-host/ink.js +97 -0
- package/dist/react-host/jsx-runtime.js +95 -0
- package/dist/react-ink.js +3834 -0
- package/dist/runtime.js +4521 -0
- package/dist/sarvam-mcp.js +960 -0
- package/dist/shell-app.js +12435 -0
- package/dist/smithy.js +8133 -0
- package/dist/swarm.js +8918 -0
- package/dist/tracing.js +761 -0
- package/dist/tui.js +2213 -0
- package/dist/types/capabilities/backends/index.d.ts +10 -0
- package/dist/types/capabilities/backends/node-backends.d.ts +30 -0
- package/dist/types/capabilities/capabilities.test.d.ts +14 -0
- package/dist/types/capabilities/files/diff.d.ts +49 -0
- package/dist/types/capabilities/files/edit.d.ts +21 -0
- package/dist/types/capabilities/files/ls.d.ts +16 -0
- package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
- package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
- package/dist/types/capabilities/files/read.d.ts +15 -0
- package/dist/types/capabilities/files/write.d.ts +16 -0
- package/dist/types/capabilities/index.d.ts +29 -0
- package/dist/types/capabilities/kernel/backends.d.ts +119 -0
- package/dist/types/capabilities/kernel/context.d.ts +52 -0
- package/dist/types/capabilities/kernel/index.d.ts +24 -0
- package/dist/types/capabilities/kernel/output.d.ts +40 -0
- package/dist/types/capabilities/kernel/registry.d.ts +61 -0
- package/dist/types/capabilities/kernel/spec.d.ts +168 -0
- package/dist/types/capabilities/planning/todo.d.ts +62 -0
- package/dist/types/capabilities/registry.d.ts +44 -0
- package/dist/types/capabilities/search/find.d.ts +33 -0
- package/dist/types/capabilities/search/grep.d.ts +30 -0
- package/dist/types/capabilities/shell/bash.d.ts +18 -0
- package/dist/types/capabilities/shell/process.d.ts +33 -0
- package/dist/types/capabilities/web/webfetch.d.ts +25 -0
- package/dist/types/capabilities/web/websearch.d.ts +23 -0
- package/dist/types/connectors-saas/connectors-saas.test.d.ts +12 -0
- package/dist/types/connectors-saas/index.d.ts +16 -0
- package/dist/types/connectors-saas/saas/adapter/composio-backend.d.ts +60 -0
- package/dist/types/connectors-saas/saas/control/connect.d.ts +114 -0
- package/dist/types/connectors-saas/saas/control/tools.d.ts +67 -0
- package/dist/types/connectors-saas/saas/core/builder.d.ts +86 -0
- package/dist/types/connectors-saas/saas/core/cache.d.ts +53 -0
- package/dist/types/connectors-saas/saas/core/index.d.ts +14 -0
- package/dist/types/connectors-saas/saas/core/port.d.ts +157 -0
- package/dist/types/connectors-saas/saas/core/scope-planner.d.ts +74 -0
- package/dist/types/connectors-saas/saas/gateway.d.ts +133 -0
- package/dist/types/connectors-saas/saas/render/format.d.ts +54 -0
- package/dist/types/connectors-saas/saas/render/index.d.ts +19 -0
- package/dist/types/connectors-saas/saas/render/summarizers.d.ts +58 -0
- package/dist/types/connectors-sarvam/connect.d.ts +14 -0
- package/dist/types/connectors-sarvam/connectors-sarvam.test.d.ts +14 -0
- package/dist/types/connectors-sarvam/index.d.ts +14 -0
- package/dist/types/connectors-sarvam/toolbox.d.ts +19 -0
- package/dist/types/connectors-sarvam/types.d.ts +49 -0
- package/dist/types/connectors-zoho/connect.d.ts +22 -0
- package/dist/types/connectors-zoho/connectors-zoho.test.d.ts +10 -0
- package/dist/types/connectors-zoho/index.d.ts +13 -0
- package/dist/types/connectors-zoho/toolbox.d.ts +19 -0
- package/dist/types/connectors-zoho/types.d.ts +56 -0
- package/dist/types/facade/agent.d.ts +1 -0
- package/dist/types/facade/ai.d.ts +1 -0
- package/dist/types/facade/bot/actions/bash.d.ts +71 -0
- package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
- package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/composio/accounts.d.ts +22 -0
- package/dist/types/facade/bot/actions/composio/client.d.ts +39 -0
- package/dist/types/facade/bot/actions/composio/connect.d.ts +19 -0
- package/dist/types/facade/bot/actions/composio/enable.d.ts +25 -0
- package/dist/types/facade/bot/actions/composio/execute.d.ts +26 -0
- package/dist/types/facade/bot/actions/composio/helpers.d.ts +16 -0
- package/dist/types/facade/bot/actions/composio/index.d.ts +9 -0
- package/dist/types/facade/bot/actions/composio/provider.d.ts +8 -0
- package/dist/types/facade/bot/actions/composio/toolkits.d.ts +22 -0
- package/dist/types/facade/bot/actions/composio/tools.d.ts +28 -0
- package/dist/types/facade/bot/actions/composio/types.d.ts +137 -0
- package/dist/types/facade/bot/actions/crew/activity-tracker.d.ts +65 -0
- package/dist/types/facade/bot/actions/crew/cleanup.d.ts +50 -0
- package/dist/types/facade/bot/actions/crew/fs-lock.d.ts +11 -0
- package/dist/types/facade/bot/actions/crew/index.d.ts +11 -0
- package/dist/types/facade/bot/actions/crew/mailbox.d.ts +20 -0
- package/dist/types/facade/bot/actions/crew/model-policy.d.ts +22 -0
- package/dist/types/facade/bot/actions/crew/names.d.ts +27 -0
- package/dist/types/facade/bot/actions/crew/protocol.d.ts +74 -0
- package/dist/types/facade/bot/actions/crew/task-store.d.ts +88 -0
- package/dist/types/facade/bot/actions/crew/team-attach-claim.d.ts +35 -0
- package/dist/types/facade/bot/actions/crew/team-config.d.ts +54 -0
- package/dist/types/facade/bot/actions/crew/worktree.d.ts +39 -0
- package/dist/types/facade/bot/actions/edit-diff.d.ts +66 -0
- package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
- package/dist/types/facade/bot/actions/edit.d.ts +59 -0
- package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/find.d.ts +27 -0
- package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/grep.d.ts +53 -0
- package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/index.d.ts +316 -0
- package/dist/types/facade/bot/actions/kit/hook-runner.d.ts +12 -0
- package/dist/types/facade/bot/actions/kit/image-resize.d.ts +19 -0
- package/dist/types/facade/bot/actions/kit/mime.d.ts +1 -0
- package/dist/types/facade/bot/actions/kit/shell.d.ts +12 -0
- package/dist/types/facade/bot/actions/ls.d.ts +41 -0
- package/dist/types/facade/bot/actions/path-utils.d.ts +8 -0
- package/dist/types/facade/bot/actions/process-controller.d.ts +14 -0
- package/dist/types/facade/bot/actions/process-manager.d.ts +59 -0
- package/dist/types/facade/bot/actions/process-types.d.ts +73 -0
- package/dist/types/facade/bot/actions/process.d.ts +37 -0
- package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
- package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/read.d.ts +43 -0
- package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/registry.d.ts +20 -0
- package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
- package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/todo-store.d.ts +76 -0
- package/dist/types/facade/bot/actions/todo-types.d.ts +72 -0
- package/dist/types/facade/bot/actions/todo.d.ts +76 -0
- package/dist/types/facade/bot/actions/truncate.d.ts +78 -0
- package/dist/types/facade/bot/actions/webfetch.d.ts +35 -0
- package/dist/types/facade/bot/actions/websearch.d.ts +37 -0
- package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/write.d.ts +45 -0
- package/dist/types/facade/bot/agent-loop.d.ts +30 -0
- package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
- package/dist/types/facade/bot/agent.d.ts +140 -0
- package/dist/types/facade/bot/error-handler.d.ts +7 -0
- package/dist/types/facade/bot/event-bus.d.ts +8 -0
- package/dist/types/facade/bot/index.d.ts +7 -0
- package/dist/types/facade/bot/messages.d.ts +83 -0
- package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
- package/dist/types/facade/bot/proxy.d.ts +70 -0
- package/dist/types/facade/bot/session-manager.d.ts +409 -0
- package/dist/types/facade/bot/state-manager.d.ts +21 -0
- package/dist/types/facade/bot/telemetry.d.ts +6 -0
- package/dist/types/facade/bot/types.d.ts +267 -0
- package/dist/types/facade/mcp-core/client-pool.d.ts +105 -0
- package/dist/types/facade/mcp-core/client.d.ts +245 -0
- package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
- package/dist/types/facade/mcp-core/config.d.ts +60 -0
- package/dist/types/facade/mcp-core/errors.d.ts +103 -0
- package/dist/types/facade/mcp-core/index.d.ts +59 -0
- package/dist/types/facade/mcp-core/oauth/flow.d.ts +72 -0
- package/dist/types/facade/mcp-core/oauth/index.d.ts +18 -0
- package/dist/types/facade/mcp-core/oauth/oauth.test.d.ts +8 -0
- package/dist/types/facade/mcp-core/oauth/provider.d.ts +51 -0
- package/dist/types/facade/mcp-core/oauth/token-store.d.ts +31 -0
- package/dist/types/facade/mcp-core/oauth/types.d.ts +96 -0
- package/dist/types/facade/mcp-core/schema-converter.d.ts +67 -0
- package/dist/types/facade/mcp-core/server.d.ts +110 -0
- package/dist/types/facade/mcp-core/tool-factory.d.ts +62 -0
- package/dist/types/facade/mcp-core/toolbox-bridge.d.ts +46 -0
- package/dist/types/facade/mcp-core/toolbox-bridge.test.d.ts +8 -0
- package/dist/types/facade/mcp-core/types.d.ts +298 -0
- package/dist/types/facade/mcp.d.ts +1 -0
- package/dist/types/facade/memory.d.ts +1 -0
- package/dist/types/facade/ml/adapters/amazon-bedrock.d.ts +32 -0
- package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
- package/dist/types/facade/ml/adapters/anthropic.d.ts +78 -0
- package/dist/types/facade/ml/adapters/azure-openai-responses.d.ts +14 -0
- package/dist/types/facade/ml/adapters/google-shared.d.ts +99 -0
- package/dist/types/facade/ml/adapters/google-vertex.d.ts +14 -0
- package/dist/types/facade/ml/adapters/google.d.ts +15 -0
- package/dist/types/facade/ml/adapters/kimi.d.ts +21 -0
- package/dist/types/facade/ml/adapters/mock.d.ts +9 -0
- package/dist/types/facade/ml/adapters/nvidia.d.ts +12 -0
- package/dist/types/facade/ml/adapters/openai-codex-responses.d.ts +8 -0
- package/dist/types/facade/ml/adapters/openai-completions.d.ts +24 -0
- package/dist/types/facade/ml/adapters/openai-responses-shared.d.ts +25 -0
- package/dist/types/facade/ml/adapters/openai-responses.d.ts +12 -0
- package/dist/types/facade/ml/adapters/openai-scaffold.d.ts +3 -0
- package/dist/types/facade/ml/adapters/register-builtins.d.ts +2 -0
- package/dist/types/facade/ml/adapters/simple-options.d.ts +39 -0
- package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
- package/dist/types/facade/ml/adapters/transform-messages.d.ts +23 -0
- package/dist/types/facade/ml/api-registry.d.ts +49 -0
- package/dist/types/facade/ml/cli.d.ts +2 -0
- package/dist/types/facade/ml/env-api-keys.d.ts +19 -0
- package/dist/types/facade/ml/index.d.ts +27 -0
- package/dist/types/facade/ml/kit/auth/anthropic.d.ts +17 -0
- package/dist/types/facade/ml/kit/auth/github-copilot.d.ts +22 -0
- package/dist/types/facade/ml/kit/auth/index.d.ts +50 -0
- package/dist/types/facade/ml/kit/auth/kimi.d.ts +23 -0
- package/dist/types/facade/ml/kit/auth/oauth-page.d.ts +2 -0
- package/dist/types/facade/ml/kit/auth/openai-codex.d.ts +36 -0
- package/dist/types/facade/ml/kit/auth/pkce.d.ts +12 -0
- package/dist/types/facade/ml/kit/auth/types.d.ts +67 -0
- package/dist/types/facade/ml/kit/base-stream-handler.d.ts +30 -0
- package/dist/types/facade/ml/kit/event-stream.d.ts +63 -0
- package/dist/types/facade/ml/kit/index.d.ts +14 -0
- package/dist/types/facade/ml/kit/json-parse.d.ts +23 -0
- package/dist/types/facade/ml/kit/message-transform.d.ts +32 -0
- package/dist/types/facade/ml/kit/output-factory.d.ts +8 -0
- package/dist/types/facade/ml/kit/overflow.d.ts +9 -0
- package/dist/types/facade/ml/kit/provider-adapter.d.ts +56 -0
- package/dist/types/facade/ml/kit/provider-client-builder.d.ts +36 -0
- package/dist/types/facade/ml/kit/provider-consolidation.d.ts +3 -0
- package/dist/types/facade/ml/kit/provider-constants.d.ts +34 -0
- package/dist/types/facade/ml/kit/provider-errors.d.ts +52 -0
- package/dist/types/facade/ml/kit/sanitize-unicode.d.ts +2 -0
- package/dist/types/facade/ml/kit/stream-event-helper.d.ts +38 -0
- package/dist/types/facade/ml/kit/stream-handler-types.d.ts +46 -0
- package/dist/types/facade/ml/kit/streaming-state-manager.d.ts +42 -0
- package/dist/types/facade/ml/kit/tool-converter.d.ts +49 -0
- package/dist/types/facade/ml/kit/typebox-helpers.d.ts +24 -0
- package/dist/types/facade/ml/kit/validation.d.ts +26 -0
- package/dist/types/facade/ml/models.d.ts +41 -0
- package/dist/types/facade/ml/models.generated.d.ts +12405 -0
- package/dist/types/facade/ml/stream.d.ts +27 -0
- package/dist/types/facade/ml/types.d.ts +269 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/interop/index.d.ts +12 -0
- package/dist/types/interop/interop.test.d.ts +19 -0
- package/dist/types/interop/protocol-bridge/bridge.d.ts +52 -0
- package/dist/types/interop/protocol-bridge/contract.d.ts +249 -0
- package/dist/types/interop/protocol-bridge/endpoint.d.ts +124 -0
- package/dist/types/interop/protocol-bridge/fleet.d.ts +92 -0
- package/dist/types/interop/protocol-bridge/host.d.ts +61 -0
- package/dist/types/interop/protocol-bridge/index.d.ts +16 -0
- package/dist/types/interop/protocol-bridge/schema.d.ts +29 -0
- package/dist/types/llmgateway/catalog/cards.d.ts +18 -0
- package/dist/types/llmgateway/catalog/cost.d.ts +15 -0
- package/dist/types/llmgateway/catalog/index.d.ts +7 -0
- package/dist/types/llmgateway/catalog/query.d.ts +40 -0
- package/dist/types/llmgateway/connectors/anthropic.d.ts +35 -0
- package/dist/types/llmgateway/connectors/azure-openai.d.ts +33 -0
- package/dist/types/llmgateway/connectors/bedrock.d.ts +113 -0
- package/dist/types/llmgateway/connectors/google-vertex.d.ts +38 -0
- package/dist/types/llmgateway/connectors/google.d.ts +40 -0
- package/dist/types/llmgateway/connectors/index.d.ts +26 -0
- package/dist/types/llmgateway/connectors/kimi.d.ts +28 -0
- package/dist/types/llmgateway/connectors/mock.d.ts +37 -0
- package/dist/types/llmgateway/connectors/nvidia.d.ts +27 -0
- package/dist/types/llmgateway/connectors/ollama.d.ts +45 -0
- package/dist/types/llmgateway/connectors/openai-chat.d.ts +25 -0
- package/dist/types/llmgateway/connectors/openai-responses.d.ts +49 -0
- package/dist/types/llmgateway/contract/connector.d.ts +32 -0
- package/dist/types/llmgateway/contract/conversation.d.ts +86 -0
- package/dist/types/llmgateway/contract/emission.d.ts +55 -0
- package/dist/types/llmgateway/contract/errors.d.ts +27 -0
- package/dist/types/llmgateway/contract/index.d.ts +13 -0
- package/dist/types/llmgateway/contract/model-card.d.ts +42 -0
- package/dist/types/llmgateway/contract/options.d.ts +31 -0
- package/dist/types/llmgateway/contract/reply.d.ts +33 -0
- package/dist/types/llmgateway/conversion/index.d.ts +10 -0
- package/dist/types/llmgateway/conversion/mappers.d.ts +65 -0
- package/dist/types/llmgateway/conversion/openai-compatible.d.ts +46 -0
- package/dist/types/llmgateway/conversion/reduce.d.ts +20 -0
- package/dist/types/llmgateway/credentials/oauth.d.ts +73 -0
- package/dist/types/llmgateway/credentials/pkce.d.ts +52 -0
- package/dist/types/llmgateway/credentials/secrets.d.ts +76 -0
- package/dist/types/llmgateway/gateway.d.ts +34 -0
- package/dist/types/llmgateway/gateway.test.d.ts +14 -0
- package/dist/types/llmgateway/index.d.ts +11 -0
- package/dist/types/llmgateway/streaming/channel.d.ts +24 -0
- package/dist/types/llmgateway/streaming/index.d.ts +10 -0
- package/dist/types/llmgateway/streaming/ndjson.d.ts +17 -0
- package/dist/types/llmgateway/streaming/sse.d.ts +31 -0
- package/dist/types/react-host/index.d.ts +9 -0
- package/dist/types/react-host/ink.d.ts +5 -0
- package/dist/types/react-host/jsx-runtime.d.ts +4 -0
- package/dist/types/react-host/loader.d.ts +3 -0
- package/dist/types/react-ink/components/ChangelogBlock.d.ts +8 -0
- package/dist/types/react-ink/components/DisplayBlockView.d.ts +8 -0
- package/dist/types/react-ink/components/Footer.d.ts +11 -0
- package/dist/types/react-ink/components/MessageList.d.ts +14 -0
- package/dist/types/react-ink/components/MessageRow.d.ts +13 -0
- package/dist/types/react-ink/components/StatusLine.d.ts +18 -0
- package/dist/types/react-ink/components/TaskPanel.d.ts +11 -0
- package/dist/types/react-ink/components/ToolEventBlock.d.ts +48 -0
- package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
- package/dist/types/react-ink/components/dialogs/DialogFrame.d.ts +8 -0
- package/dist/types/react-ink/components/dialogs/LoginDialog.d.ts +14 -0
- package/dist/types/react-ink/components/dialogs/ModelDialog.d.ts +9 -0
- package/dist/types/react-ink/components/dialogs/OAuthDialog.d.ts +9 -0
- package/dist/types/react-ink/components/dialogs/ScopedModelsDialog.d.ts +9 -0
- package/dist/types/react-ink/components/dialogs/SelectableDialog.d.ts +22 -0
- package/dist/types/react-ink/components/dialogs/SessionDialog.d.ts +13 -0
- package/dist/types/react-ink/components/dialogs/SettingsDialog.d.ts +14 -0
- package/dist/types/react-ink/components/dialogs/StartupSessionPicker.d.ts +9 -0
- package/dist/types/react-ink/components/dialogs/ThemeDialog.d.ts +26 -0
- package/dist/types/react-ink/components/dialogs/TreeDialog.d.ts +8 -0
- package/dist/types/react-ink/components/dialogs/UserMessageDialog.d.ts +8 -0
- package/dist/types/react-ink/components/messages/AssistantMessage.d.ts +12 -0
- package/dist/types/react-ink/components/messages/BashMessage.d.ts +8 -0
- package/dist/types/react-ink/components/messages/BranchSummaryMessage.d.ts +8 -0
- package/dist/types/react-ink/components/messages/CompactionMessage.d.ts +8 -0
- package/dist/types/react-ink/components/messages/CustomMessage.d.ts +9 -0
- package/dist/types/react-ink/components/messages/SkillInvocationMessage.d.ts +7 -0
- package/dist/types/react-ink/components/messages/ToolCallMessage.d.ts +8 -0
- package/dist/types/react-ink/components/messages/ToolResultBlock.d.ts +12 -0
- package/dist/types/react-ink/components/messages/UserMessage.d.ts +9 -0
- package/dist/types/react-ink/diff/Diff.d.ts +22 -0
- package/dist/types/react-ink/diff/diff.test.d.ts +1 -0
- package/dist/types/react-ink/diff/structured.d.ts +41 -0
- package/dist/types/react-ink/diff/word-diff.d.ts +27 -0
- package/dist/types/react-ink/index.d.ts +42 -0
- package/dist/types/react-ink/markdown/Markdown.d.ts +23 -0
- package/dist/types/react-ink/markdown/MarkdownTable.d.ts +19 -0
- package/dist/types/react-ink/markdown/StreamingMarkdown.d.ts +34 -0
- package/dist/types/react-ink/markdown/format-token.d.ts +39 -0
- package/dist/types/react-ink/markdown/highlight.d.ts +31 -0
- package/dist/types/react-ink/theme-adapter.d.ts +66 -0
- package/dist/types/react-ink/types.d.ts +133 -0
- package/dist/types/react-ink/utils/message-groups.d.ts +25 -0
- package/dist/types/react-ink/utils/selection-dialog.d.ts +2 -0
- package/dist/types/react-ink/utils/session-browser.d.ts +27 -0
- package/dist/types/react-ink/utils/tool-display.d.ts +37 -0
- package/dist/types/runtime/cadence/cadence.test.d.ts +6 -0
- package/dist/types/runtime/cadence/fold.d.ts +48 -0
- package/dist/types/runtime/cadence/index.d.ts +12 -0
- package/dist/types/runtime/cadence/reducer.d.ts +44 -0
- package/dist/types/runtime/conductor/agent.d.ts +71 -0
- package/dist/types/runtime/conductor/index.d.ts +11 -0
- package/dist/types/runtime/contract/config.d.ts +44 -0
- package/dist/types/runtime/contract/effect.d.ts +41 -0
- package/dist/types/runtime/contract/errors.d.ts +22 -0
- package/dist/types/runtime/contract/events.d.ts +63 -0
- package/dist/types/runtime/contract/index.d.ts +18 -0
- package/dist/types/runtime/contract/run-state.d.ts +57 -0
- package/dist/types/runtime/contract/session.d.ts +42 -0
- package/dist/types/runtime/contract/signal.d.ts +49 -0
- package/dist/types/runtime/contract/tools.d.ts +45 -0
- package/dist/types/runtime/dispatch/index.d.ts +9 -0
- package/dist/types/runtime/dispatch/scheduler.d.ts +53 -0
- package/dist/types/runtime/index.d.ts +22 -0
- package/dist/types/runtime/ledger/accumulator.d.ts +44 -0
- package/dist/types/runtime/ledger/bus.d.ts +50 -0
- package/dist/types/runtime/ledger/index.d.ts +11 -0
- package/dist/types/runtime/memory/compactor.d.ts +56 -0
- package/dist/types/runtime/memory/estimate.d.ts +21 -0
- package/dist/types/runtime/memory/index.d.ts +8 -0
- package/dist/types/runtime/runtime.test.d.ts +15 -0
- package/dist/types/runtime/store/dag.d.ts +72 -0
- package/dist/types/runtime/store/hash.d.ts +18 -0
- package/dist/types/runtime/store/index.d.ts +12 -0
- package/dist/types/runtime/store/persist.d.ts +49 -0
- package/dist/types/runtime/turn/driver.d.ts +32 -0
- package/dist/types/runtime/turn/index.d.ts +8 -0
- package/dist/types/runtime/wire/index.d.ts +10 -0
- package/dist/types/runtime/wire/projectors.d.ts +53 -0
- package/dist/types/shell-app/auth-cli/index.d.ts +9 -0
- package/dist/types/shell-app/auth-cli/oauth-cli.d.ts +73 -0
- package/dist/types/shell-app/boot/context.d.ts +108 -0
- package/dist/types/shell-app/boot/index.d.ts +13 -0
- package/dist/types/shell-app/boot/pipeline.d.ts +66 -0
- package/dist/types/shell-app/boot/stages.d.ts +77 -0
- package/dist/types/shell-app/cli.d.ts +75 -0
- package/dist/types/shell-app/config/index.d.ts +11 -0
- package/dist/types/shell-app/config/locator.d.ts +85 -0
- package/dist/types/shell-app/config/settings.d.ts +91 -0
- package/dist/types/shell-app/index.d.ts +26 -0
- package/dist/types/shell-app/invocation/flags.d.ts +89 -0
- package/dist/types/shell-app/invocation/parse.d.ts +89 -0
- package/dist/types/shell-app/locate/brand.d.ts +71 -0
- package/dist/types/shell-app/locate/index.d.ts +12 -0
- package/dist/types/shell-app/locate/locator.d.ts +122 -0
- package/dist/types/shell-app/runners/contract.d.ts +116 -0
- package/dist/types/shell-app/runners/index.d.ts +15 -0
- package/dist/types/shell-app/runners/one-shot.d.ts +21 -0
- package/dist/types/shell-app/runners/registry.d.ts +34 -0
- package/dist/types/shell-app/runners/repl.d.ts +51 -0
- package/dist/types/shell-app/runners/wire.d.ts +34 -0
- package/dist/types/shell-app/shell-app.test.d.ts +14 -0
- package/dist/types/shell-app/upgrade/index.d.ts +9 -0
- package/dist/types/shell-app/upgrade/upgrades.d.ts +62 -0
- package/dist/types/smithy/config/flag-reader.d.ts +128 -0
- package/dist/types/smithy/config/index.d.ts +9 -0
- package/dist/types/smithy/forge.d.ts +135 -0
- package/dist/types/smithy/index.d.ts +36 -0
- package/dist/types/smithy/knowledge/index.d.ts +10 -0
- package/dist/types/smithy/knowledge/loader.d.ts +64 -0
- package/dist/types/smithy/persona/blueprint.d.ts +75 -0
- package/dist/types/smithy/persona/define-agent.d.ts +51 -0
- package/dist/types/smithy/persona/index.d.ts +14 -0
- package/dist/types/smithy/persona/profiles.d.ts +44 -0
- package/dist/types/smithy/runtime/index.d.ts +9 -0
- package/dist/types/smithy/runtime/tool-ledger.d.ts +130 -0
- package/dist/types/smithy/smithy.test.d.ts +20 -0
- package/dist/types/smithy/ui/index.d.ts +11 -0
- package/dist/types/smithy/ui/transcript.d.ts +183 -0
- package/dist/types/swarm/coordinator.d.ts +192 -0
- package/dist/types/swarm/index.d.ts +33 -0
- package/dist/types/swarm/isolation/index.d.ts +14 -0
- package/dist/types/swarm/isolation/runner.d.ts +61 -0
- package/dist/types/swarm/isolation/worktree.d.ts +142 -0
- package/dist/types/swarm/kernel/faults.d.ts +52 -0
- package/dist/types/swarm/kernel/ids.d.ts +25 -0
- package/dist/types/swarm/kernel/index.d.ts +26 -0
- package/dist/types/swarm/kernel/json-cell.d.ts +113 -0
- package/dist/types/swarm/kernel/jsonl-log.d.ts +81 -0
- package/dist/types/swarm/postbox/channel.d.ts +88 -0
- package/dist/types/swarm/postbox/codecs.d.ts +253 -0
- package/dist/types/swarm/postbox/index.d.ts +16 -0
- package/dist/types/swarm/roster/index.d.ts +16 -0
- package/dist/types/swarm/roster/manifest.d.ts +185 -0
- package/dist/types/swarm/swarm.test.d.ts +24 -0
- package/dist/types/swarm/telemetry/activity.d.ts +143 -0
- package/dist/types/swarm/workboard/board.d.ts +174 -0
- package/dist/types/swarm/workboard/dep-graph.d.ts +77 -0
- package/dist/types/swarm/workboard/index.d.ts +12 -0
- package/dist/types/tracing/adapter/index.d.ts +12 -0
- package/dist/types/tracing/adapter/runtime-trace.d.ts +54 -0
- package/dist/types/tracing/channel/index.d.ts +9 -0
- package/dist/types/tracing/channel/signal.d.ts +93 -0
- package/dist/types/tracing/index.d.ts +25 -0
- package/dist/types/tracing/recorder/index.d.ts +12 -0
- package/dist/types/tracing/recorder/recorder.d.ts +75 -0
- package/dist/types/tracing/recorder/sampling.d.ts +55 -0
- package/dist/types/tracing/redaction/index.d.ts +8 -0
- package/dist/types/tracing/redaction/secret-scrubber.d.ts +95 -0
- package/dist/types/tracing/registry/hub.d.ts +57 -0
- package/dist/types/tracing/registry/index.d.ts +7 -0
- package/dist/types/tracing/signal/handle.d.ts +73 -0
- package/dist/types/tracing/signal/index.d.ts +12 -0
- package/dist/types/tracing/signal/segment.d.ts +124 -0
- package/dist/types/tracing/sinks/base.d.ts +51 -0
- package/dist/types/tracing/sinks/console.d.ts +44 -0
- package/dist/types/tracing/sinks/file.d.ts +47 -0
- package/dist/types/tracing/sinks/index.d.ts +16 -0
- package/dist/types/tracing/sinks/stream.d.ts +46 -0
- package/dist/types/tracing/tracing.test.d.ts +7 -0
- package/dist/types/ui/autocomplete.d.ts +59 -0
- package/dist/types/ui/contracts.d.ts +38 -0
- package/dist/types/ui/editor-component.d.ts +100 -0
- package/dist/types/ui/fuzzy.d.ts +29 -0
- package/dist/types/ui/index.d.ts +8 -0
- package/dist/types/ui/keybindings.d.ts +62 -0
- package/dist/types/ui/keys.d.ts +171 -0
- package/dist/types/ui/theme-types.d.ts +36 -0
- package/dist/types/ui/utils.d.ts +101 -0
- package/dist/types/ui-bridge/InteractiveApp.d.ts +38 -0
- package/dist/types/ui-bridge/adapter.d.ts +90 -0
- package/dist/types/ui-bridge/index.d.ts +37 -0
- package/dist/zoho.js +1451 -0
- package/package.json +48 -48
package/dist/zoho.js
ADDED
|
@@ -0,0 +1,1451 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/facade/mcp-core/client.ts
|
|
9
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
10
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
11
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
12
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
13
|
+
import {
|
|
14
|
+
UnauthorizedError
|
|
15
|
+
} from "@modelcontextprotocol/sdk/client/auth.js";
|
|
16
|
+
import {
|
|
17
|
+
ListRootsRequestSchema,
|
|
18
|
+
ElicitRequestSchema,
|
|
19
|
+
CreateMessageRequestSchema,
|
|
20
|
+
PingRequestSchema,
|
|
21
|
+
ToolListChangedNotificationSchema,
|
|
22
|
+
ResourceListChangedNotificationSchema,
|
|
23
|
+
ResourceUpdatedNotificationSchema,
|
|
24
|
+
PromptListChangedNotificationSchema,
|
|
25
|
+
ProgressNotificationSchema,
|
|
26
|
+
LoggingMessageNotificationSchema,
|
|
27
|
+
CancelledNotificationSchema
|
|
28
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
29
|
+
|
|
30
|
+
// src/facade/mcp-core/errors.ts
|
|
31
|
+
var MCPError = class extends Error {
|
|
32
|
+
/** Error code */
|
|
33
|
+
code;
|
|
34
|
+
/** Additional error details */
|
|
35
|
+
details;
|
|
36
|
+
/** Server name where error occurred */
|
|
37
|
+
serverName;
|
|
38
|
+
/** Tool name if error occurred during tool execution */
|
|
39
|
+
toolName;
|
|
40
|
+
constructor(message, code, details, options) {
|
|
41
|
+
super(message, options?.cause ? { cause: options.cause } : void 0);
|
|
42
|
+
this.name = "MCPError";
|
|
43
|
+
this.code = code;
|
|
44
|
+
this.details = details;
|
|
45
|
+
this.serverName = options?.serverName;
|
|
46
|
+
this.toolName = options?.toolName;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Convert error to JSON for logging/serialization.
|
|
50
|
+
*/
|
|
51
|
+
toJSON() {
|
|
52
|
+
return {
|
|
53
|
+
name: this.name,
|
|
54
|
+
message: this.message,
|
|
55
|
+
code: this.code,
|
|
56
|
+
details: this.details,
|
|
57
|
+
serverName: this.serverName,
|
|
58
|
+
toolName: this.toolName
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a string representation of the error.
|
|
63
|
+
*/
|
|
64
|
+
toString() {
|
|
65
|
+
let str = `${this.name} [${this.code}]: ${this.message}`;
|
|
66
|
+
if (this.serverName) str += ` (server: ${this.serverName})`;
|
|
67
|
+
if (this.toolName) str += ` (tool: ${this.toolName})`;
|
|
68
|
+
return str;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
function createServerError(message, serverName, details) {
|
|
72
|
+
return new MCPError(message, "SERVER_ERROR" /* SERVER_ERROR */, details, { serverName });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/facade/mcp-core/client.ts
|
|
76
|
+
var DEFAULT_REQUEST_TIMEOUT = 6e4;
|
|
77
|
+
var DEFAULT_CONNECT_TIMEOUT = 3e4;
|
|
78
|
+
var MCPClient = class {
|
|
79
|
+
constructor(options) {
|
|
80
|
+
this.options = options;
|
|
81
|
+
this.serverName = options.name;
|
|
82
|
+
this.config = options.config;
|
|
83
|
+
this.timeout = options.timeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
84
|
+
this.logHandler = options.logger;
|
|
85
|
+
this.enableServerLogs = options.enableServerLogs ?? true;
|
|
86
|
+
this.enableProgressTracking = options.enableProgressTracking ?? false;
|
|
87
|
+
this._roots = options.roots ?? [];
|
|
88
|
+
}
|
|
89
|
+
options;
|
|
90
|
+
client;
|
|
91
|
+
transport;
|
|
92
|
+
isConnected = false;
|
|
93
|
+
connectionPromise = null;
|
|
94
|
+
serverCapabilities;
|
|
95
|
+
logHandler;
|
|
96
|
+
enableServerLogs;
|
|
97
|
+
enableProgressTracking;
|
|
98
|
+
_roots;
|
|
99
|
+
// --- Host-overridable handlers (Core of #13) --------------------------------
|
|
100
|
+
// Server-initiated REQUESTS are answered by request handlers registered on the
|
|
101
|
+
// SDK client; the handlers delegate to these host-supplied callbacks when set,
|
|
102
|
+
// and fall back to sensible defaults otherwise.
|
|
103
|
+
elicitationHandler;
|
|
104
|
+
samplingHandler;
|
|
105
|
+
// Server-initiated NOTIFICATIONS are dispatched to these (so the host can, for
|
|
106
|
+
// example, re-list tools when a `tools/list_changed` arrives).
|
|
107
|
+
resourceUpdatedHandler;
|
|
108
|
+
resourceListChangedHandler;
|
|
109
|
+
toolListChangedHandler;
|
|
110
|
+
promptListChangedHandler;
|
|
111
|
+
progressHandler;
|
|
112
|
+
/** Server name */
|
|
113
|
+
serverName;
|
|
114
|
+
/** Server config */
|
|
115
|
+
config;
|
|
116
|
+
/** Request timeout */
|
|
117
|
+
timeout;
|
|
118
|
+
// ========================================================================
|
|
119
|
+
// Connection Lifecycle
|
|
120
|
+
// ========================================================================
|
|
121
|
+
/**
|
|
122
|
+
* Connect to the MCP server.
|
|
123
|
+
* Safe to call multiple times - returns existing connection if already connected.
|
|
124
|
+
*/
|
|
125
|
+
async connect() {
|
|
126
|
+
if (this.isConnected) return;
|
|
127
|
+
if (this.connectionPromise) {
|
|
128
|
+
return this.connectionPromise;
|
|
129
|
+
}
|
|
130
|
+
this.connectionPromise = this.doConnect();
|
|
131
|
+
try {
|
|
132
|
+
await this.connectionPromise;
|
|
133
|
+
} finally {
|
|
134
|
+
this.connectionPromise = null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async doConnect() {
|
|
138
|
+
const transport = this.createTransport(this.config);
|
|
139
|
+
this.transport = transport;
|
|
140
|
+
const client = new Client(
|
|
141
|
+
{ name: "indusagi-coding-agent", version: "0.13.0" },
|
|
142
|
+
{
|
|
143
|
+
capabilities: {
|
|
144
|
+
roots: { listChanged: true },
|
|
145
|
+
elicitation: {},
|
|
146
|
+
sampling: {}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
this.client = client;
|
|
151
|
+
this.registerServerHandlers(client);
|
|
152
|
+
const prevOnClose = transport.onclose;
|
|
153
|
+
transport.onclose = () => {
|
|
154
|
+
this.isConnected = false;
|
|
155
|
+
this.log("debug", "Transport closed");
|
|
156
|
+
prevOnClose?.();
|
|
157
|
+
};
|
|
158
|
+
try {
|
|
159
|
+
await client.connect(transport, { timeout: DEFAULT_CONNECT_TIMEOUT });
|
|
160
|
+
} catch (err) {
|
|
161
|
+
const recovered = await this.tryFinishOAuthAndReconnect(client, transport, err);
|
|
162
|
+
if (!recovered) {
|
|
163
|
+
throw this.wrapConnectError(err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
this.serverCapabilities = client.getServerCapabilities();
|
|
167
|
+
this.isConnected = true;
|
|
168
|
+
this.log("info", `Connected to MCP server`);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* If `err` is an OAuth challenge and we have a provider + finishAuth-capable
|
|
172
|
+
* transport, wait for the browser code, exchange tokens, then reconnect on a
|
|
173
|
+
* **fresh** transport (the first transport is already `start()`ed and cannot
|
|
174
|
+
* be reused — see StreamableHTTPClientTransport.start).
|
|
175
|
+
*
|
|
176
|
+
* Matches the official SDK CLI example (`simpleOAuthClient`):
|
|
177
|
+
* finishAuth(code) → new StreamableHTTPClientTransport → client.connect again
|
|
178
|
+
*
|
|
179
|
+
* Client.connect already called `close()` on init failure, so the Client is
|
|
180
|
+
* free to attach a new transport.
|
|
181
|
+
*/
|
|
182
|
+
async tryFinishOAuthAndReconnect(client, transport, err) {
|
|
183
|
+
if (!isAuthRequiredError(err)) return false;
|
|
184
|
+
const provider = this.options.authProvider;
|
|
185
|
+
if (!provider) return false;
|
|
186
|
+
const finishAuth = transport.finishAuth;
|
|
187
|
+
const waitForCode = provider.waitForAuthorizationCode;
|
|
188
|
+
if (typeof finishAuth !== "function" || typeof waitForCode !== "function") {
|
|
189
|
+
this.log(
|
|
190
|
+
"error",
|
|
191
|
+
"MCP server requires authentication but OAuth finish path is incomplete (need authProvider.waitForAuthorizationCode + transport.finishAuth)."
|
|
192
|
+
);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
this.log("info", "MCP server requires OAuth \u2014 complete login in the browser\u2026");
|
|
196
|
+
try {
|
|
197
|
+
const code = await waitForCode.call(provider);
|
|
198
|
+
this.log("info", "OAuth authorization code received \u2014 exchanging tokens\u2026");
|
|
199
|
+
await finishAuth.call(transport, code);
|
|
200
|
+
try {
|
|
201
|
+
await transport.close?.();
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
const fresh = this.createTransport(this.config);
|
|
205
|
+
this.transport = fresh;
|
|
206
|
+
const prevOnClose = fresh.onclose;
|
|
207
|
+
fresh.onclose = () => {
|
|
208
|
+
this.isConnected = false;
|
|
209
|
+
this.log("debug", "Transport closed");
|
|
210
|
+
prevOnClose?.();
|
|
211
|
+
};
|
|
212
|
+
this.log("info", "Reconnecting MCP session with OAuth tokens\u2026");
|
|
213
|
+
await client.connect(fresh, { timeout: DEFAULT_CONNECT_TIMEOUT });
|
|
214
|
+
return true;
|
|
215
|
+
} catch (oauthErr) {
|
|
216
|
+
this.log(
|
|
217
|
+
"error",
|
|
218
|
+
`OAuth finish failed: ${oauthErr instanceof Error ? oauthErr.message : String(oauthErr)}`
|
|
219
|
+
);
|
|
220
|
+
throw this.wrapConnectError(oauthErr);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** Turn raw SDK / network errors into a clearer MCPError for callers. */
|
|
224
|
+
wrapConnectError(err) {
|
|
225
|
+
if (err instanceof MCPError) return err;
|
|
226
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
227
|
+
if (isAuthRequiredError(err)) {
|
|
228
|
+
return new MCPError(
|
|
229
|
+
`Authentication required for MCP server "${this.serverName}". Pass auth: "auto" (default) to run the OAuth browser flow, or set a Bearer token via headers. Original: ${message}`,
|
|
230
|
+
"CONNECTION_FAILED" /* CONNECTION_FAILED */,
|
|
231
|
+
err,
|
|
232
|
+
{ serverName: this.serverName, cause: err instanceof Error ? err : void 0 }
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return new MCPError(
|
|
236
|
+
`Failed to connect to MCP server "${this.serverName}": ${message}`,
|
|
237
|
+
"CONNECTION_FAILED" /* CONNECTION_FAILED */,
|
|
238
|
+
err,
|
|
239
|
+
{ serverName: this.serverName, cause: err instanceof Error ? err : void 0 }
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Build the SDK transport for the configured server.
|
|
244
|
+
*
|
|
245
|
+
* - stdio: `StdioClientTransport` (inherits the parent env, merges config.env).
|
|
246
|
+
* - http: `StreamableHTTPClientTransport` (POST + server-push SSE channel,
|
|
247
|
+
* reconnect + session-expiry handled by the SDK), unless the config
|
|
248
|
+
* asks for the legacy `SSEClientTransport`.
|
|
249
|
+
*/
|
|
250
|
+
createTransport(config) {
|
|
251
|
+
if ("command" in config) {
|
|
252
|
+
return new StdioClientTransport({
|
|
253
|
+
command: config.command,
|
|
254
|
+
args: config.args ?? [],
|
|
255
|
+
env: { ...sanitizeEnv(process.env), ...config.env ?? {} },
|
|
256
|
+
cwd: config.cwd,
|
|
257
|
+
stderr: "pipe"
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if ("url" in config) {
|
|
261
|
+
const requestInit = config.headers ? { headers: config.headers } : void 0;
|
|
262
|
+
if (config.transport === "sse") {
|
|
263
|
+
return new SSEClientTransport(config.url, {
|
|
264
|
+
requestInit,
|
|
265
|
+
...this.options.authProvider ? { authProvider: this.options.authProvider } : {}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return new StreamableHTTPClientTransport(config.url, {
|
|
269
|
+
requestInit,
|
|
270
|
+
...this.options.authProvider ? { authProvider: this.options.authProvider } : {}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
throw new MCPError(
|
|
274
|
+
"Server configuration must include either a command or a url",
|
|
275
|
+
"CONFIG_ERROR" /* CONFIG_ERROR */,
|
|
276
|
+
void 0,
|
|
277
|
+
{ serverName: this.serverName }
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Register handlers for server-initiated REQUESTS and NOTIFICATIONS.
|
|
282
|
+
*
|
|
283
|
+
* REQUESTS (have both `.method` AND `.id`) must be ANSWERED with a JSON-RPC
|
|
284
|
+
* RESPONSE carrying the matching id — the SDK does this automatically for the
|
|
285
|
+
* value a request handler returns (or rejects). The previous hand-rolled
|
|
286
|
+
* transport routed these into the notification path, so they were never
|
|
287
|
+
* answered (bug #13).
|
|
288
|
+
*
|
|
289
|
+
* Defaults:
|
|
290
|
+
* - ping → {}
|
|
291
|
+
* - roots/list → the configured roots
|
|
292
|
+
* - elicitation/create→ decline ({action:"cancel"}) unless a host handler is set
|
|
293
|
+
* - sampling → reject "Method not found" unless a host handler is set
|
|
294
|
+
*/
|
|
295
|
+
registerServerHandlers(client) {
|
|
296
|
+
client.setRequestHandler(PingRequestSchema, async () => ({}));
|
|
297
|
+
client.setRequestHandler(ListRootsRequestSchema, async () => ({
|
|
298
|
+
roots: this._roots.map((r) => ({ uri: r.uri, name: r.name }))
|
|
299
|
+
}));
|
|
300
|
+
client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
301
|
+
if (!this.elicitationHandler) {
|
|
302
|
+
return { action: "cancel" };
|
|
303
|
+
}
|
|
304
|
+
const params = request.params;
|
|
305
|
+
const elicitRequest = {
|
|
306
|
+
message: params.message,
|
|
307
|
+
requestedSchema: params.requestedSchema ?? {}
|
|
308
|
+
};
|
|
309
|
+
const result = await this.elicitationHandler(elicitRequest);
|
|
310
|
+
return mapToSdkElicitResult(result);
|
|
311
|
+
});
|
|
312
|
+
client.setRequestHandler(CreateMessageRequestSchema, async (request) => {
|
|
313
|
+
if (!this.samplingHandler) {
|
|
314
|
+
throw new MCPError(
|
|
315
|
+
"Method not found: sampling/createMessage",
|
|
316
|
+
"SERVER_ERROR" /* SERVER_ERROR */,
|
|
317
|
+
{ method: "sampling/createMessage" },
|
|
318
|
+
{ serverName: this.serverName }
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
return await this.samplingHandler(request.params);
|
|
322
|
+
});
|
|
323
|
+
client.setNotificationHandler(ToolListChangedNotificationSchema, () => {
|
|
324
|
+
this.log("debug", "Tool list changed");
|
|
325
|
+
this.toolListChangedHandler?.();
|
|
326
|
+
});
|
|
327
|
+
client.setNotificationHandler(ResourceListChangedNotificationSchema, () => {
|
|
328
|
+
this.log("debug", "Resource list changed");
|
|
329
|
+
this.resourceListChangedHandler?.();
|
|
330
|
+
});
|
|
331
|
+
client.setNotificationHandler(ResourceUpdatedNotificationSchema, (n) => {
|
|
332
|
+
const uri = n.params?.uri;
|
|
333
|
+
this.log("debug", `Resource updated: ${uri}`);
|
|
334
|
+
if (uri) this.resourceUpdatedHandler?.({ uri });
|
|
335
|
+
});
|
|
336
|
+
client.setNotificationHandler(PromptListChangedNotificationSchema, () => {
|
|
337
|
+
this.log("debug", "Prompt list changed");
|
|
338
|
+
this.promptListChangedHandler?.();
|
|
339
|
+
});
|
|
340
|
+
client.setNotificationHandler(ProgressNotificationSchema, (n) => {
|
|
341
|
+
const params = n.params;
|
|
342
|
+
this.log("debug", `Progress: ${JSON.stringify(params)}`);
|
|
343
|
+
if (this.progressHandler && params) {
|
|
344
|
+
const notification = {
|
|
345
|
+
progressToken: params.progressToken,
|
|
346
|
+
progress: params.progress,
|
|
347
|
+
total: params.total,
|
|
348
|
+
message: params.message
|
|
349
|
+
};
|
|
350
|
+
this.progressHandler(notification);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
client.setNotificationHandler(LoggingMessageNotificationSchema, (n) => {
|
|
354
|
+
if (this.enableServerLogs && n.params) {
|
|
355
|
+
const { level, ...rest } = n.params;
|
|
356
|
+
this.log(level || "info", "[SERVER]", rest);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
client.setNotificationHandler(CancelledNotificationSchema, (n) => {
|
|
360
|
+
this.log("debug", `Request cancelled: ${JSON.stringify(n.params)}`);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Disconnect from the MCP server.
|
|
365
|
+
*
|
|
366
|
+
* The SDK's `client.close()` closes the transport and rejects any pending
|
|
367
|
+
* requests — a clean teardown for both stdio (graceful subprocess shutdown)
|
|
368
|
+
* and HTTP (close the push channel / end the session).
|
|
369
|
+
*/
|
|
370
|
+
async disconnect() {
|
|
371
|
+
if (!this.client) {
|
|
372
|
+
this.log("debug", "Disconnect called but not connected");
|
|
373
|
+
this.isConnected = false;
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
this.log("debug", "Disconnecting from MCP server");
|
|
377
|
+
try {
|
|
378
|
+
await this.client.close();
|
|
379
|
+
} catch (error) {
|
|
380
|
+
this.log("error", `Error during disconnect: ${error}`);
|
|
381
|
+
} finally {
|
|
382
|
+
this.client = void 0;
|
|
383
|
+
this.transport = void 0;
|
|
384
|
+
this.isConnected = false;
|
|
385
|
+
this.log("debug", "Successfully disconnected");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Whether the client is connected.
|
|
390
|
+
*/
|
|
391
|
+
get connected() {
|
|
392
|
+
return this.isConnected;
|
|
393
|
+
}
|
|
394
|
+
// ========================================================================
|
|
395
|
+
// Tool Operations
|
|
396
|
+
// ========================================================================
|
|
397
|
+
/**
|
|
398
|
+
* List all tools available from the server.
|
|
399
|
+
*/
|
|
400
|
+
async listTools() {
|
|
401
|
+
this.ensureConnected();
|
|
402
|
+
const result = await this.client.listTools(void 0, {
|
|
403
|
+
timeout: this.timeout
|
|
404
|
+
});
|
|
405
|
+
return result.tools;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Call a tool on the server.
|
|
409
|
+
*/
|
|
410
|
+
async callTool(name, args) {
|
|
411
|
+
this.ensureConnected();
|
|
412
|
+
this.log("debug", `Calling tool: ${name}`);
|
|
413
|
+
try {
|
|
414
|
+
const result = await this.client.callTool(
|
|
415
|
+
{ name, arguments: args },
|
|
416
|
+
void 0,
|
|
417
|
+
{ timeout: this.timeout }
|
|
418
|
+
);
|
|
419
|
+
this.log("debug", `Tool ${name} executed successfully`);
|
|
420
|
+
return result;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
this.log("error", `Tool ${name} failed: ${error}`);
|
|
423
|
+
throw this.wrapError(error);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// ========================================================================
|
|
427
|
+
// Resource Operations
|
|
428
|
+
// ========================================================================
|
|
429
|
+
/**
|
|
430
|
+
* List all resources available from the server.
|
|
431
|
+
*/
|
|
432
|
+
async listResources() {
|
|
433
|
+
this.ensureConnected();
|
|
434
|
+
if (!this.serverCapabilities?.resources) {
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
const result = await this.client.listResources(void 0, {
|
|
438
|
+
timeout: this.timeout
|
|
439
|
+
});
|
|
440
|
+
return result.resources;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Read a resource from the server.
|
|
444
|
+
*/
|
|
445
|
+
async readResource(uri) {
|
|
446
|
+
this.ensureConnected();
|
|
447
|
+
return await this.client.readResource({ uri }, { timeout: this.timeout });
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Subscribe to resource updates.
|
|
451
|
+
*/
|
|
452
|
+
async subscribeResource(uri) {
|
|
453
|
+
this.ensureConnected();
|
|
454
|
+
await this.client.subscribeResource({ uri }, { timeout: this.timeout });
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Unsubscribe from resource updates.
|
|
458
|
+
*/
|
|
459
|
+
async unsubscribeResource(uri) {
|
|
460
|
+
this.ensureConnected();
|
|
461
|
+
await this.client.unsubscribeResource({ uri }, { timeout: this.timeout });
|
|
462
|
+
}
|
|
463
|
+
// ========================================================================
|
|
464
|
+
// Prompt Operations
|
|
465
|
+
// ========================================================================
|
|
466
|
+
/**
|
|
467
|
+
* List all prompts available from the server.
|
|
468
|
+
*/
|
|
469
|
+
async listPrompts() {
|
|
470
|
+
this.ensureConnected();
|
|
471
|
+
if (!this.serverCapabilities?.prompts) {
|
|
472
|
+
return [];
|
|
473
|
+
}
|
|
474
|
+
const result = await this.client.listPrompts(void 0, {
|
|
475
|
+
timeout: this.timeout
|
|
476
|
+
});
|
|
477
|
+
return result.prompts;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get a prompt from the server.
|
|
481
|
+
*/
|
|
482
|
+
async getPrompt(name, args) {
|
|
483
|
+
this.ensureConnected();
|
|
484
|
+
return await this.client.getPrompt(
|
|
485
|
+
{ name, arguments: args },
|
|
486
|
+
{ timeout: this.timeout }
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
// ========================================================================
|
|
490
|
+
// Roots Operations
|
|
491
|
+
// ========================================================================
|
|
492
|
+
/**
|
|
493
|
+
* Get the configured roots.
|
|
494
|
+
*/
|
|
495
|
+
get roots() {
|
|
496
|
+
return [...this._roots];
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Update the roots and notify the server.
|
|
500
|
+
*/
|
|
501
|
+
async setRoots(roots) {
|
|
502
|
+
this.log("debug", `Updating roots to ${roots.length} entries`);
|
|
503
|
+
this._roots = [...roots];
|
|
504
|
+
if (this.isConnected && this.client) {
|
|
505
|
+
try {
|
|
506
|
+
await this.client.sendRootsListChanged();
|
|
507
|
+
} catch (error) {
|
|
508
|
+
this.log("debug", `Failed to send roots/list_changed: ${error}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
// ========================================================================
|
|
513
|
+
// Handler Registration
|
|
514
|
+
//
|
|
515
|
+
// Setters STORE the host handler (and, for notifications, the SDK handler
|
|
516
|
+
// registered in `registerServerHandlers` delegates to the stored callback).
|
|
517
|
+
// This is the host-overridable layer at the core of bug #13.
|
|
518
|
+
// ========================================================================
|
|
519
|
+
/**
|
|
520
|
+
* Set a handler for resource updated notifications.
|
|
521
|
+
*/
|
|
522
|
+
setResourceUpdatedHandler(handler) {
|
|
523
|
+
this.resourceUpdatedHandler = handler;
|
|
524
|
+
this.log("debug", "Resource updated handler registered");
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Set a handler for resource list changed notifications.
|
|
528
|
+
*/
|
|
529
|
+
setResourceListChangedHandler(handler) {
|
|
530
|
+
this.resourceListChangedHandler = handler;
|
|
531
|
+
this.log("debug", "Resource list changed handler registered");
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Set a handler for tool list changed notifications.
|
|
535
|
+
*/
|
|
536
|
+
setToolListChangedHandler(handler) {
|
|
537
|
+
this.toolListChangedHandler = handler;
|
|
538
|
+
this.log("debug", "Tool list changed handler registered");
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Set a handler for prompt list changed notifications.
|
|
542
|
+
*/
|
|
543
|
+
setPromptListChangedHandler(handler) {
|
|
544
|
+
this.promptListChangedHandler = handler;
|
|
545
|
+
this.log("debug", "Prompt list changed handler registered");
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Set a handler for elicitation requests. When set, the server's
|
|
549
|
+
* `elicitation/create` REQUEST is answered with the host's decision; when
|
|
550
|
+
* unset the request is declined ({action:"cancel"}).
|
|
551
|
+
*/
|
|
552
|
+
setElicitationHandler(handler) {
|
|
553
|
+
this.elicitationHandler = handler;
|
|
554
|
+
this.log("debug", "Elicitation handler registered");
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Set a handler for sampling (`sampling/createMessage`) requests. When set,
|
|
558
|
+
* the server request is answered with the host's result; when unset the
|
|
559
|
+
* request is rejected with "Method not found".
|
|
560
|
+
*/
|
|
561
|
+
setSamplingHandler(handler) {
|
|
562
|
+
this.samplingHandler = handler;
|
|
563
|
+
this.log("debug", "Sampling handler registered");
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Set a handler for progress notifications.
|
|
567
|
+
*/
|
|
568
|
+
setProgressHandler(handler) {
|
|
569
|
+
this.progressHandler = handler;
|
|
570
|
+
this.log("debug", "Progress handler registered");
|
|
571
|
+
}
|
|
572
|
+
// ========================================================================
|
|
573
|
+
// Private Methods
|
|
574
|
+
// ========================================================================
|
|
575
|
+
ensureConnected() {
|
|
576
|
+
if (!this.isConnected || !this.client) {
|
|
577
|
+
throw new MCPError(
|
|
578
|
+
"Not connected to MCP server",
|
|
579
|
+
"NOT_CONNECTED" /* NOT_CONNECTED */,
|
|
580
|
+
void 0,
|
|
581
|
+
{ serverName: this.serverName }
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Normalize an SDK/transport error into an MCPError. Session-expiry style
|
|
587
|
+
* failures (404 session / -32001) are surfaced as SESSION_ERROR so callers
|
|
588
|
+
* (the pool) can decide to reconnect; the SDK's StreamableHTTP transport
|
|
589
|
+
* already attempts a bounded reconnect re-using the session id before this.
|
|
590
|
+
*/
|
|
591
|
+
wrapError(error) {
|
|
592
|
+
if (error instanceof MCPError) return error;
|
|
593
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
594
|
+
const code = error.code;
|
|
595
|
+
if (code === -32001 || /\b404\b|session/i.test(message)) {
|
|
596
|
+
return new MCPError(message, "SESSION_ERROR" /* SESSION_ERROR */, { code }, {
|
|
597
|
+
serverName: this.serverName
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
return new MCPError(message, "SERVER_ERROR" /* SERVER_ERROR */, { code }, {
|
|
601
|
+
serverName: this.serverName
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
log(level, message, details) {
|
|
605
|
+
const msg = `[${this.serverName}] ${message}`;
|
|
606
|
+
if (this.logHandler) {
|
|
607
|
+
this.logHandler({
|
|
608
|
+
level,
|
|
609
|
+
message: msg,
|
|
610
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
611
|
+
serverName: this.serverName,
|
|
612
|
+
details
|
|
613
|
+
});
|
|
614
|
+
} else {
|
|
615
|
+
if (process.env.INDUSAGI_DEBUG || level === "error") {
|
|
616
|
+
const prefix = `[MCP:${level.toUpperCase()}]`;
|
|
617
|
+
if (details) {
|
|
618
|
+
if (level === "error") {
|
|
619
|
+
console.error(prefix, msg, details);
|
|
620
|
+
} else {
|
|
621
|
+
console.log(prefix, msg, details);
|
|
622
|
+
}
|
|
623
|
+
} else {
|
|
624
|
+
if (level === "error") {
|
|
625
|
+
console.error(prefix, msg);
|
|
626
|
+
} else {
|
|
627
|
+
console.log(prefix, msg);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
function mapToSdkElicitResult(result) {
|
|
635
|
+
if (result.action === "accept") {
|
|
636
|
+
return {
|
|
637
|
+
action: "accept",
|
|
638
|
+
content: result.content ?? {}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
return { action: result.action };
|
|
642
|
+
}
|
|
643
|
+
function isAuthRequiredError(err) {
|
|
644
|
+
if (err == null) return false;
|
|
645
|
+
if (err instanceof UnauthorizedError) return true;
|
|
646
|
+
if (typeof err === "object") {
|
|
647
|
+
const code = err.code;
|
|
648
|
+
if (code === 401 || code === "401") return true;
|
|
649
|
+
}
|
|
650
|
+
if (err instanceof Error) {
|
|
651
|
+
const msg = err.message;
|
|
652
|
+
if (/unauthorized/i.test(msg)) return true;
|
|
653
|
+
if (/authentication required/i.test(msg)) return true;
|
|
654
|
+
if (/auth required/i.test(msg)) return true;
|
|
655
|
+
if (err.name === "UnauthorizedError") return true;
|
|
656
|
+
}
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
function sanitizeEnv(env) {
|
|
660
|
+
const out = {};
|
|
661
|
+
for (const [k, v] of Object.entries(env)) {
|
|
662
|
+
if (typeof v === "string") out[k] = v;
|
|
663
|
+
}
|
|
664
|
+
return out;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// src/facade/mcp-core/oauth/flow.ts
|
|
668
|
+
import { createServer } from "node:http";
|
|
669
|
+
var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
670
|
+
var SUCCESS_HTML = '<!doctype html><html><body style="font-family:system-ui;padding:2rem"><h1>Authorization complete</h1><p>You can close this tab and return to your terminal.</p></body></html>';
|
|
671
|
+
var ERROR_HTML = (msg) => `<!doctype html><html><body style="font-family:system-ui;padding:2rem"><h1>Authorization failed</h1><pre>${escapeHtml(msg)}</pre></body></html>`;
|
|
672
|
+
function escapeHtml(s) {
|
|
673
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
674
|
+
}
|
|
675
|
+
async function startOAuthCallbackServer(options) {
|
|
676
|
+
const timeoutMs = options?.timeoutMs ?? CALLBACK_TIMEOUT_MS;
|
|
677
|
+
const logger = options?.logger;
|
|
678
|
+
let settled = false;
|
|
679
|
+
let timer;
|
|
680
|
+
let resolveCode;
|
|
681
|
+
let rejectCode;
|
|
682
|
+
const codePromise = new Promise((res, rej) => {
|
|
683
|
+
resolveCode = res;
|
|
684
|
+
rejectCode = rej;
|
|
685
|
+
});
|
|
686
|
+
codePromise.catch(() => {
|
|
687
|
+
});
|
|
688
|
+
let expectedState;
|
|
689
|
+
const clearTimer = () => {
|
|
690
|
+
if (timer !== void 0) {
|
|
691
|
+
clearTimeout(timer);
|
|
692
|
+
timer = void 0;
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
const settleReject = (err) => {
|
|
696
|
+
if (settled) return;
|
|
697
|
+
settled = true;
|
|
698
|
+
clearTimer();
|
|
699
|
+
try {
|
|
700
|
+
server.close();
|
|
701
|
+
} catch {
|
|
702
|
+
}
|
|
703
|
+
rejectCode(err);
|
|
704
|
+
};
|
|
705
|
+
const settleResolve = (code) => {
|
|
706
|
+
if (settled) return;
|
|
707
|
+
settled = true;
|
|
708
|
+
clearTimer();
|
|
709
|
+
resolveCode(code);
|
|
710
|
+
setTimeout(() => {
|
|
711
|
+
try {
|
|
712
|
+
server.close();
|
|
713
|
+
} catch {
|
|
714
|
+
}
|
|
715
|
+
}, 50);
|
|
716
|
+
};
|
|
717
|
+
const server = createServer((req, res) => {
|
|
718
|
+
if (settled) {
|
|
719
|
+
res.statusCode = 410;
|
|
720
|
+
res.end("already used");
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
handleRequest(
|
|
724
|
+
req,
|
|
725
|
+
res,
|
|
726
|
+
expectedState,
|
|
727
|
+
(code) => settleResolve(code),
|
|
728
|
+
(err) => settleReject(err),
|
|
729
|
+
logger
|
|
730
|
+
);
|
|
731
|
+
});
|
|
732
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
733
|
+
server.once("error", rejectListen);
|
|
734
|
+
server.listen(0, "127.0.0.1", () => resolveListen());
|
|
735
|
+
});
|
|
736
|
+
const address = server.address();
|
|
737
|
+
if (!address) {
|
|
738
|
+
server.close();
|
|
739
|
+
throw new Error("oauth-callback: server bound to no address");
|
|
740
|
+
}
|
|
741
|
+
const port = address.port;
|
|
742
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
743
|
+
const close = () => {
|
|
744
|
+
if (!settled) {
|
|
745
|
+
settleReject(new Error("oauth-callback: closed before browser login completed"));
|
|
746
|
+
} else {
|
|
747
|
+
clearTimer();
|
|
748
|
+
try {
|
|
749
|
+
server.close();
|
|
750
|
+
} catch {
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
return {
|
|
755
|
+
redirectUri,
|
|
756
|
+
port,
|
|
757
|
+
waitForCode(state) {
|
|
758
|
+
expectedState = state;
|
|
759
|
+
logger?.debug(`oauth-callback: waiting for browser at ${redirectUri}`);
|
|
760
|
+
if (!settled && timer === void 0) {
|
|
761
|
+
timer = setTimeout(() => {
|
|
762
|
+
settleReject(
|
|
763
|
+
new Error(
|
|
764
|
+
`oauth-callback: timed out after ${timeoutMs}ms waiting for browser login at ${redirectUri}`
|
|
765
|
+
)
|
|
766
|
+
);
|
|
767
|
+
}, timeoutMs);
|
|
768
|
+
timer.unref();
|
|
769
|
+
}
|
|
770
|
+
return codePromise;
|
|
771
|
+
},
|
|
772
|
+
close
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function handleRequest(req, res, expectedState, onCode, onError, logger) {
|
|
776
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
777
|
+
if (url.pathname !== "/callback") {
|
|
778
|
+
if (url.pathname === "/favicon.ico") {
|
|
779
|
+
res.statusCode = 404;
|
|
780
|
+
res.end();
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
res.statusCode = 404;
|
|
784
|
+
res.end("not found");
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const errorParam = url.searchParams.get("error");
|
|
788
|
+
if (errorParam) {
|
|
789
|
+
const desc = url.searchParams.get("error_description") ?? "";
|
|
790
|
+
logger?.warn(`oauth-callback: server returned error ${errorParam} ${desc}`);
|
|
791
|
+
res.statusCode = 400;
|
|
792
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
793
|
+
res.end(ERROR_HTML(`${errorParam}${desc ? ": " + desc : ""}`));
|
|
794
|
+
onError(new Error(`OAuth provider returned error: ${errorParam}${desc ? " \u2014 " + desc : ""}`));
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const code = url.searchParams.get("code");
|
|
798
|
+
const state = url.searchParams.get("state");
|
|
799
|
+
if (!code) {
|
|
800
|
+
res.statusCode = 400;
|
|
801
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
802
|
+
res.end(ERROR_HTML("missing code"));
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (expectedState && state && state !== expectedState) {
|
|
806
|
+
res.statusCode = 400;
|
|
807
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
808
|
+
res.end(ERROR_HTML("state mismatch \u2014 possible CSRF"));
|
|
809
|
+
onError(new Error("OAuth callback state did not match \u2014 refusing to exchange code"));
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
res.statusCode = 200;
|
|
813
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
814
|
+
res.end(SUCCESS_HTML);
|
|
815
|
+
logger?.debug(`oauth-callback: received authorization code (${code.length} chars)`);
|
|
816
|
+
onCode(code);
|
|
817
|
+
}
|
|
818
|
+
async function openBrowser(url) {
|
|
819
|
+
try {
|
|
820
|
+
const mod = await import("node:child_process").catch(() => null);
|
|
821
|
+
if (!mod) return false;
|
|
822
|
+
const cmd = platformOpenCommand(url);
|
|
823
|
+
if (!cmd) return false;
|
|
824
|
+
const child = mod.spawn(cmd.cmd, cmd.args, { detached: true, stdio: "ignore" });
|
|
825
|
+
child.unref();
|
|
826
|
+
return true;
|
|
827
|
+
} catch {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function platformOpenCommand(url) {
|
|
832
|
+
switch (process.platform) {
|
|
833
|
+
case "darwin":
|
|
834
|
+
return { cmd: "open", args: [url] };
|
|
835
|
+
case "win32":
|
|
836
|
+
return { cmd: "rundll32", args: ["url.dll,FileProtocolHandler", url] };
|
|
837
|
+
default:
|
|
838
|
+
return { cmd: "xdg-open", args: [url] };
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/facade/mcp-core/oauth/token-store.ts
|
|
843
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
844
|
+
import { dirname, join } from "node:path";
|
|
845
|
+
function defaultCredentialsDir() {
|
|
846
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
847
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
848
|
+
if (xdg && xdg.length > 0) return join(xdg, "indusagi", "credentials", "mcp");
|
|
849
|
+
if (home.length > 0) return join(home, ".config", "indusagi", "credentials", "mcp");
|
|
850
|
+
return join(process.cwd(), ".indusagi", "credentials", "mcp");
|
|
851
|
+
}
|
|
852
|
+
function resolveCredentialsDir(override) {
|
|
853
|
+
return override && override.length > 0 ? override : defaultCredentialsDir();
|
|
854
|
+
}
|
|
855
|
+
function credentialsPath(credentialsDir, id) {
|
|
856
|
+
const safe = id.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
857
|
+
return join(credentialsDir, `${safe}.json`);
|
|
858
|
+
}
|
|
859
|
+
function readMcpOAuthState(credentialsDir, id) {
|
|
860
|
+
const path = credentialsPath(credentialsDir, id);
|
|
861
|
+
if (!existsSync(path)) return void 0;
|
|
862
|
+
let raw;
|
|
863
|
+
try {
|
|
864
|
+
raw = readFileSync(path, "utf8");
|
|
865
|
+
} catch (e) {
|
|
866
|
+
return void 0;
|
|
867
|
+
}
|
|
868
|
+
let parsed;
|
|
869
|
+
try {
|
|
870
|
+
parsed = JSON.parse(raw);
|
|
871
|
+
} catch (e) {
|
|
872
|
+
return void 0;
|
|
873
|
+
}
|
|
874
|
+
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
875
|
+
return { id, updatedAt: Date.now(), ...parsed };
|
|
876
|
+
}
|
|
877
|
+
function writeMcpOAuthState(credentialsDir, state) {
|
|
878
|
+
const path = credentialsPath(credentialsDir, state.id);
|
|
879
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
880
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
881
|
+
const json = JSON.stringify({ ...state, updatedAt: Date.now() }, null, 2);
|
|
882
|
+
writeFileSync(tmp, json, { mode: 384 });
|
|
883
|
+
renameSync(tmp, path);
|
|
884
|
+
try {
|
|
885
|
+
chmodSync(path, 384);
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function clearMcpOAuthState(credentialsDir, id) {
|
|
890
|
+
const path = credentialsPath(credentialsDir, id);
|
|
891
|
+
try {
|
|
892
|
+
const fs = __require("node:fs");
|
|
893
|
+
fs.rmSync(path, { force: true });
|
|
894
|
+
} catch {
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// src/facade/mcp-core/oauth/provider.ts
|
|
899
|
+
function randomState() {
|
|
900
|
+
const bytes = new Uint8Array(16);
|
|
901
|
+
crypto.getRandomValues(bytes);
|
|
902
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
903
|
+
}
|
|
904
|
+
function toSdkTokens(t) {
|
|
905
|
+
return {
|
|
906
|
+
access_token: t.access_token,
|
|
907
|
+
refresh_token: t.refresh_token,
|
|
908
|
+
scope: t.scope,
|
|
909
|
+
token_type: t.token_type ?? "Bearer",
|
|
910
|
+
expires_in: t.expires_at !== void 0 ? Math.max(0, Math.floor((t.expires_at - Date.now()) / 1e3)) : void 0
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
function toSdkClientInfo(c) {
|
|
914
|
+
return {
|
|
915
|
+
client_id: c.client_id,
|
|
916
|
+
client_secret: c.client_secret,
|
|
917
|
+
client_id_issued_at: c.client_id_issued_at,
|
|
918
|
+
client_secret_expires_at: c.client_secret_expires_at
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
async function createMcpOAuthProvider(config) {
|
|
922
|
+
const credentialsDir = resolveCredentialsDir(config.credentialsDir);
|
|
923
|
+
const openBrowserEnabled = config.openBrowser ?? Boolean(process.env.CI !== "true" && process.stdout.isTTY);
|
|
924
|
+
let callback;
|
|
925
|
+
let redirectUrl;
|
|
926
|
+
if (config.redirectUrl) {
|
|
927
|
+
redirectUrl = config.redirectUrl;
|
|
928
|
+
} else {
|
|
929
|
+
callback = await startOAuthCallbackServer({
|
|
930
|
+
logger: config.logger ? { debug: config.logger.debug, warn: config.logger.warn } : void 0
|
|
931
|
+
});
|
|
932
|
+
redirectUrl = callback.redirectUri;
|
|
933
|
+
}
|
|
934
|
+
const clientMetadata = {
|
|
935
|
+
client_name: config.clientMetadata?.client_name ?? "indusagi-mcp-client",
|
|
936
|
+
client_uri: config.clientMetadata?.client_uri,
|
|
937
|
+
logo_uri: config.clientMetadata?.logo_uri,
|
|
938
|
+
contacts: config.clientMetadata?.contacts ? [...config.clientMetadata.contacts] : void 0,
|
|
939
|
+
tos_uri: config.clientMetadata?.tos_uri,
|
|
940
|
+
policy_uri: config.clientMetadata?.policy_uri,
|
|
941
|
+
redirect_uris: config.clientMetadata?.redirect_uris ? [...config.clientMetadata.redirect_uris] : [redirectUrl],
|
|
942
|
+
token_endpoint_auth_method: config.clientMetadata?.token_endpoint_auth_method ?? "none",
|
|
943
|
+
grant_types: config.clientMetadata?.grant_types ? [...config.clientMetadata.grant_types] : ["authorization_code", "refresh_token"],
|
|
944
|
+
response_types: config.clientMetadata?.response_types ? [...config.clientMetadata.response_types] : ["code"],
|
|
945
|
+
scope: config.clientMetadata?.scope
|
|
946
|
+
};
|
|
947
|
+
let lastAuthUrl;
|
|
948
|
+
let lastState;
|
|
949
|
+
let pendingCode;
|
|
950
|
+
const provider = {
|
|
951
|
+
id: config.id,
|
|
952
|
+
credentialsDir,
|
|
953
|
+
redirectUrl,
|
|
954
|
+
get clientMetadata() {
|
|
955
|
+
return clientMetadata;
|
|
956
|
+
},
|
|
957
|
+
state() {
|
|
958
|
+
const s = randomState();
|
|
959
|
+
lastState = s;
|
|
960
|
+
return s;
|
|
961
|
+
},
|
|
962
|
+
clientInformation() {
|
|
963
|
+
const state = readMcpOAuthState(credentialsDir, config.id);
|
|
964
|
+
return state?.client ? toSdkClientInfo(state.client) : void 0;
|
|
965
|
+
},
|
|
966
|
+
saveClientInformation(client) {
|
|
967
|
+
const state = readMcpOAuthState(credentialsDir, config.id) ?? {
|
|
968
|
+
id: config.id,
|
|
969
|
+
updatedAt: Date.now()
|
|
970
|
+
};
|
|
971
|
+
writeMcpOAuthState(credentialsDir, {
|
|
972
|
+
...state,
|
|
973
|
+
client: {
|
|
974
|
+
client_id: client.client_id,
|
|
975
|
+
client_secret: client.client_secret,
|
|
976
|
+
client_id_issued_at: client.client_id_issued_at,
|
|
977
|
+
client_secret_expires_at: client.client_secret_expires_at
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
},
|
|
981
|
+
tokens() {
|
|
982
|
+
const state = readMcpOAuthState(credentialsDir, config.id);
|
|
983
|
+
return state?.tokens ? toSdkTokens(state.tokens) : void 0;
|
|
984
|
+
},
|
|
985
|
+
saveTokens(tokens) {
|
|
986
|
+
const state = readMcpOAuthState(credentialsDir, config.id) ?? {
|
|
987
|
+
id: config.id,
|
|
988
|
+
updatedAt: Date.now()
|
|
989
|
+
};
|
|
990
|
+
writeMcpOAuthState(credentialsDir, {
|
|
991
|
+
...state,
|
|
992
|
+
tokens: {
|
|
993
|
+
access_token: tokens.access_token,
|
|
994
|
+
refresh_token: tokens.refresh_token,
|
|
995
|
+
scope: tokens.scope,
|
|
996
|
+
token_type: tokens.token_type,
|
|
997
|
+
expires_at: tokens.expires_in !== void 0 ? Date.now() + Math.trunc(tokens.expires_in) * 1e3 : void 0
|
|
998
|
+
}
|
|
999
|
+
});
|
|
1000
|
+
},
|
|
1001
|
+
async redirectToAuthorization(authorizationUrl) {
|
|
1002
|
+
const url = authorizationUrl.toString();
|
|
1003
|
+
lastAuthUrl = url;
|
|
1004
|
+
try {
|
|
1005
|
+
lastState = new URL(url).searchParams.get("state") ?? lastState;
|
|
1006
|
+
} catch {
|
|
1007
|
+
}
|
|
1008
|
+
const prompt = `oauth: open this URL in your browser to authorize Zoho MCP:
|
|
1009
|
+
${url}`;
|
|
1010
|
+
config.logger?.info?.(prompt);
|
|
1011
|
+
config.logger?.debug?.(prompt);
|
|
1012
|
+
console.error(`
|
|
1013
|
+
[zoho oauth] Authorize in browser:
|
|
1014
|
+
${url}
|
|
1015
|
+
`);
|
|
1016
|
+
let opened = false;
|
|
1017
|
+
if (openBrowserEnabled) opened = await openBrowser(url);
|
|
1018
|
+
if (!opened) {
|
|
1019
|
+
const tip = "oauth: could not auto-open browser. Visit the URL above manually.";
|
|
1020
|
+
config.logger?.info?.(tip);
|
|
1021
|
+
config.logger?.debug?.(tip);
|
|
1022
|
+
console.error(`[zoho oauth] ${tip}`);
|
|
1023
|
+
} else {
|
|
1024
|
+
config.logger?.debug?.("oauth: browser open launched");
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
saveCodeVerifier(verifier) {
|
|
1028
|
+
const state = readMcpOAuthState(credentialsDir, config.id) ?? {
|
|
1029
|
+
id: config.id,
|
|
1030
|
+
updatedAt: Date.now()
|
|
1031
|
+
};
|
|
1032
|
+
writeMcpOAuthState(credentialsDir, { ...state, codeVerifier: verifier });
|
|
1033
|
+
},
|
|
1034
|
+
codeVerifier() {
|
|
1035
|
+
const state = readMcpOAuthState(credentialsDir, config.id);
|
|
1036
|
+
const verifier = state?.codeVerifier;
|
|
1037
|
+
if (!verifier) throw new Error("No PKCE code verifier saved");
|
|
1038
|
+
return verifier;
|
|
1039
|
+
},
|
|
1040
|
+
invalidateCredentials(scope) {
|
|
1041
|
+
if (scope === "all") {
|
|
1042
|
+
clearMcpOAuthState(credentialsDir, config.id);
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
const state = readMcpOAuthState(credentialsDir, config.id);
|
|
1046
|
+
if (!state) return;
|
|
1047
|
+
const next = { ...state };
|
|
1048
|
+
if (scope === "tokens" || scope === "verifier") next.tokens = void 0;
|
|
1049
|
+
if (scope === "verifier") next.codeVerifier = void 0;
|
|
1050
|
+
if (scope === "discovery") {
|
|
1051
|
+
next.resourceMetadata = void 0;
|
|
1052
|
+
next.authServerMetadata = void 0;
|
|
1053
|
+
}
|
|
1054
|
+
writeMcpOAuthState(credentialsDir, next);
|
|
1055
|
+
},
|
|
1056
|
+
async waitForAuthorizationCode() {
|
|
1057
|
+
if (pendingCode) return pendingCode;
|
|
1058
|
+
if (!callback) {
|
|
1059
|
+
throw new Error(
|
|
1060
|
+
"oauth: no local callback server (custom redirectUrl set). Pass the authorization code via finishAuth yourself."
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
config.logger?.info("oauth: waiting for browser authorization\u2026");
|
|
1064
|
+
console.error("[zoho oauth] Waiting for browser authorization\u2026");
|
|
1065
|
+
const code = await callback.waitForCode(lastState);
|
|
1066
|
+
pendingCode = code;
|
|
1067
|
+
return code;
|
|
1068
|
+
},
|
|
1069
|
+
dispose() {
|
|
1070
|
+
callback?.close();
|
|
1071
|
+
callback = void 0;
|
|
1072
|
+
},
|
|
1073
|
+
lastAuthorizationUrl() {
|
|
1074
|
+
return lastAuthUrl;
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
return provider;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// src/facade/mcp-core/oauth/index.ts
|
|
1081
|
+
import {
|
|
1082
|
+
auth,
|
|
1083
|
+
discoverOAuthServerInfo,
|
|
1084
|
+
startAuthorization,
|
|
1085
|
+
exchangeAuthorization,
|
|
1086
|
+
refreshAuthorization,
|
|
1087
|
+
registerClient,
|
|
1088
|
+
extractWWWAuthenticateParams
|
|
1089
|
+
} from "@modelcontextprotocol/sdk/client/auth.js";
|
|
1090
|
+
|
|
1091
|
+
// src/capabilities/kernel/spec.ts
|
|
1092
|
+
function coerceInput(raw) {
|
|
1093
|
+
if (typeof raw === "string") {
|
|
1094
|
+
const trimmed = raw.trim();
|
|
1095
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
1096
|
+
try {
|
|
1097
|
+
return JSON.parse(trimmed);
|
|
1098
|
+
} catch {
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return raw;
|
|
1102
|
+
}
|
|
1103
|
+
if (raw === null || raw === void 0) {
|
|
1104
|
+
return {};
|
|
1105
|
+
}
|
|
1106
|
+
return raw;
|
|
1107
|
+
}
|
|
1108
|
+
function describeThrow(err) {
|
|
1109
|
+
if (err instanceof Error) return err.message;
|
|
1110
|
+
if (typeof err === "string") return err;
|
|
1111
|
+
try {
|
|
1112
|
+
return JSON.stringify(err);
|
|
1113
|
+
} catch {
|
|
1114
|
+
return String(err);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function projectOutcome(id, result) {
|
|
1118
|
+
const blocks = result.content;
|
|
1119
|
+
let output;
|
|
1120
|
+
if (blocks.length === 1) {
|
|
1121
|
+
const only = blocks[0];
|
|
1122
|
+
output = only.kind === "text" ? only.text : only.value;
|
|
1123
|
+
} else {
|
|
1124
|
+
output = blocks;
|
|
1125
|
+
}
|
|
1126
|
+
return { id, output, isError: result.isError === true };
|
|
1127
|
+
}
|
|
1128
|
+
function defineTool(spec) {
|
|
1129
|
+
return {
|
|
1130
|
+
name: spec.name,
|
|
1131
|
+
// Facade `AgentTool` surface: top-level schema fields + an `execute` adapter
|
|
1132
|
+
// so the tool is usable by the high-level `Agent` as well as the kernel.
|
|
1133
|
+
label: spec.name,
|
|
1134
|
+
description: spec.description,
|
|
1135
|
+
parameters: spec.parameters,
|
|
1136
|
+
async execute(_toolCallId, params, signal) {
|
|
1137
|
+
const ctx = {
|
|
1138
|
+
cwd: process.cwd(),
|
|
1139
|
+
signal: signal ?? new AbortController().signal
|
|
1140
|
+
};
|
|
1141
|
+
try {
|
|
1142
|
+
const result = await spec.run(coerceInput(params), ctx);
|
|
1143
|
+
return {
|
|
1144
|
+
content: result.content.map(
|
|
1145
|
+
(block) => block.kind === "text" ? { type: "text", text: block.text } : { type: "text", text: JSON.stringify(block.value) }
|
|
1146
|
+
),
|
|
1147
|
+
details: {},
|
|
1148
|
+
isError: result.isError ?? false
|
|
1149
|
+
};
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
return {
|
|
1152
|
+
content: [{ type: "text", text: `${spec.name} did not finish: ${describeThrow(err)}` }],
|
|
1153
|
+
details: {},
|
|
1154
|
+
isError: true
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
descriptor() {
|
|
1159
|
+
return {
|
|
1160
|
+
name: spec.name,
|
|
1161
|
+
description: spec.description,
|
|
1162
|
+
parameters: spec.parameters
|
|
1163
|
+
};
|
|
1164
|
+
},
|
|
1165
|
+
async invoke(call, ctx) {
|
|
1166
|
+
if (ctx.signal.aborted) {
|
|
1167
|
+
return {
|
|
1168
|
+
id: call.id,
|
|
1169
|
+
output: `Cancelled before ${spec.name} could begin.`,
|
|
1170
|
+
isError: true
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
try {
|
|
1174
|
+
const input = coerceInput(call.input);
|
|
1175
|
+
const result = await spec.run(input, ctx);
|
|
1176
|
+
return projectOutcome(call.id, result);
|
|
1177
|
+
} catch (err) {
|
|
1178
|
+
return {
|
|
1179
|
+
id: call.id,
|
|
1180
|
+
output: `${spec.name} did not finish: ${describeThrow(err)}`,
|
|
1181
|
+
isError: true
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// src/facade/mcp-core/toolbox-bridge.ts
|
|
1189
|
+
var NOOP_FS_NOT_SUPPORTED = "not supported in MCP-wrapped tools";
|
|
1190
|
+
var noopFs = {
|
|
1191
|
+
readFile: () => {
|
|
1192
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1193
|
+
},
|
|
1194
|
+
writeFile: () => {
|
|
1195
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1196
|
+
},
|
|
1197
|
+
stat: () => {
|
|
1198
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1199
|
+
},
|
|
1200
|
+
readdir: () => {
|
|
1201
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1202
|
+
},
|
|
1203
|
+
mkdir: () => {
|
|
1204
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1205
|
+
},
|
|
1206
|
+
rm: () => {
|
|
1207
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1208
|
+
},
|
|
1209
|
+
exists: () => {
|
|
1210
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
var noopShell = {
|
|
1214
|
+
exec: () => {
|
|
1215
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1216
|
+
},
|
|
1217
|
+
spawn: () => {
|
|
1218
|
+
throw new MCPError(NOOP_FS_NOT_SUPPORTED, "CONFIG_ERROR");
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
function makeToolContext(signal) {
|
|
1222
|
+
return {
|
|
1223
|
+
cwd: process.cwd(),
|
|
1224
|
+
fs: noopFs,
|
|
1225
|
+
shell: noopShell,
|
|
1226
|
+
signal,
|
|
1227
|
+
budget: { kind: "tail", maxBytes: 5e4 }
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
function describeThrow2(err) {
|
|
1231
|
+
if (err instanceof Error) return err.message;
|
|
1232
|
+
if (typeof err === "string") return err;
|
|
1233
|
+
try {
|
|
1234
|
+
return JSON.stringify(err);
|
|
1235
|
+
} catch {
|
|
1236
|
+
return String(err);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
function projectMcpResult(result, logger) {
|
|
1240
|
+
const blocks = [];
|
|
1241
|
+
if (result.content && result.content.length > 0) {
|
|
1242
|
+
for (const block of result.content) {
|
|
1243
|
+
if (block.type === "text") {
|
|
1244
|
+
blocks.push({ kind: "text", text: block.text });
|
|
1245
|
+
} else if (block.type === "image") {
|
|
1246
|
+
const len = typeof block.data === "string" ? block.data.length : 0;
|
|
1247
|
+
blocks.push({ kind: "text", text: `[image ${block.mimeType}, ${len} chars base64]` });
|
|
1248
|
+
logger?.debug(`mcp bridge: image block collapsed to text mention (${block.mimeType})`);
|
|
1249
|
+
} else if (block.type === "resource") {
|
|
1250
|
+
blocks.push({ kind: "text", text: `[resource ${block.resource.uri}]` });
|
|
1251
|
+
} else {
|
|
1252
|
+
blocks.push({ kind: "text", text: "[unknown content block]" });
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
if (result.structuredContent !== void 0) {
|
|
1257
|
+
blocks.push({ kind: "json", value: result.structuredContent });
|
|
1258
|
+
}
|
|
1259
|
+
const content = blocks.length > 0 ? blocks : [{ kind: "text", text: "(no content)" }];
|
|
1260
|
+
return {
|
|
1261
|
+
content,
|
|
1262
|
+
...result.isError === true ? { isError: true } : {}
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
function makeRunner(client, remoteName, logger) {
|
|
1266
|
+
return async (input, ctx) => {
|
|
1267
|
+
if (ctx.signal.aborted) {
|
|
1268
|
+
return {
|
|
1269
|
+
content: [{ kind: "text", text: "Cancelled before tool could start." }],
|
|
1270
|
+
isError: true
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
try {
|
|
1274
|
+
const args = input ?? {};
|
|
1275
|
+
const result = await client.callTool(remoteName, args);
|
|
1276
|
+
return projectMcpResult(result, logger);
|
|
1277
|
+
} catch (err) {
|
|
1278
|
+
const wrapped = err instanceof MCPError ? err : createServerError(describeThrow2(err), client.serverName);
|
|
1279
|
+
logger?.error(`mcp bridge: tool "${remoteName}" failed: ${wrapped.message}`, wrapped);
|
|
1280
|
+
return {
|
|
1281
|
+
content: [{ kind: "text", text: `MCP tool failed: ${wrapped.message}` }],
|
|
1282
|
+
isError: true
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
async function mcpClientToToolBox(client, options = {}) {
|
|
1288
|
+
const prefix = options.namePrefix ?? "mcp";
|
|
1289
|
+
const logger = options.logger;
|
|
1290
|
+
const tools = await client.listTools();
|
|
1291
|
+
const filtered = typeof options.filter === "function" ? tools.filter((t) => options.filter(t)) : tools;
|
|
1292
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1293
|
+
for (const tool of filtered) {
|
|
1294
|
+
const remoteName = tool.name;
|
|
1295
|
+
const prefixed = `${prefix}__${remoteName}`;
|
|
1296
|
+
const description = tool.description && tool.description.length > 0 ? tool.description : `<tool "${remoteName}" from MCP server "${client.serverName}">`;
|
|
1297
|
+
const parameters = tool.inputSchema;
|
|
1298
|
+
const def = defineTool({
|
|
1299
|
+
name: prefixed,
|
|
1300
|
+
description,
|
|
1301
|
+
parameters,
|
|
1302
|
+
run: makeRunner(client, remoteName, logger)
|
|
1303
|
+
});
|
|
1304
|
+
byName.set(prefixed, def);
|
|
1305
|
+
}
|
|
1306
|
+
const runner = {
|
|
1307
|
+
async run(call, signal) {
|
|
1308
|
+
const def = byName.get(call.name);
|
|
1309
|
+
if (!def) {
|
|
1310
|
+
return {
|
|
1311
|
+
id: call.id,
|
|
1312
|
+
output: `No tool "${call.name}" on MCP server "${client.serverName}".`,
|
|
1313
|
+
isError: true
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
return def.invoke(call, makeToolContext(signal));
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
return {
|
|
1320
|
+
descriptors() {
|
|
1321
|
+
return Array.from(byName.values()).map((d) => d.descriptor());
|
|
1322
|
+
},
|
|
1323
|
+
runner
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// src/connectors-zoho/connect.ts
|
|
1328
|
+
async function connectZohoMcp(options) {
|
|
1329
|
+
const id = options.id ?? "zoho";
|
|
1330
|
+
const authMode = options.auth ?? "auto";
|
|
1331
|
+
let parsedUrl;
|
|
1332
|
+
try {
|
|
1333
|
+
parsedUrl = new URL(options.url);
|
|
1334
|
+
} catch {
|
|
1335
|
+
throw new Error(`connectZohoMcp: invalid url "${options.url}"`);
|
|
1336
|
+
}
|
|
1337
|
+
if (parsedUrl.protocol !== "https:" && parsedUrl.protocol !== "http:") {
|
|
1338
|
+
throw new Error(`connectZohoMcp: url must be http(s), got "${parsedUrl.protocol}"`);
|
|
1339
|
+
}
|
|
1340
|
+
let oauthProvider;
|
|
1341
|
+
if (authMode === "auto" || authMode === "oauth") {
|
|
1342
|
+
oauthProvider = await createMcpOAuthProvider({
|
|
1343
|
+
id,
|
|
1344
|
+
credentialsDir: options.tokenStorePath,
|
|
1345
|
+
redirectUrl: options.redirectUrl,
|
|
1346
|
+
// Default true outside CI so TUI sessions always get a browser redirect.
|
|
1347
|
+
openBrowser: options.openBrowser ?? (process.env.CI !== "true" && process.env.ZOHO_OPEN_BROWSER !== "0"),
|
|
1348
|
+
clientMetadata: {
|
|
1349
|
+
client_name: "indusagi-zoho-agent",
|
|
1350
|
+
scope: options.scopes?.join(" ")
|
|
1351
|
+
},
|
|
1352
|
+
logger: options.logger ? {
|
|
1353
|
+
debug: options.logger.debug,
|
|
1354
|
+
info: (msg) => {
|
|
1355
|
+
options.logger?.debug(msg);
|
|
1356
|
+
console.error(`[zoho] ${msg}`);
|
|
1357
|
+
},
|
|
1358
|
+
warn: (msg) => options.logger?.debug(msg),
|
|
1359
|
+
error: options.logger.error
|
|
1360
|
+
} : {
|
|
1361
|
+
debug: (msg) => {
|
|
1362
|
+
if (process.env.INDUSAGI_DEBUG) console.error(`[zoho debug] ${msg}`);
|
|
1363
|
+
},
|
|
1364
|
+
info: (msg) => console.error(`[zoho] ${msg}`),
|
|
1365
|
+
warn: (msg) => console.error(`[zoho] ${msg}`),
|
|
1366
|
+
error: (msg) => console.error(`[zoho error] ${msg}`)
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
const client = new MCPClient({
|
|
1371
|
+
name: id,
|
|
1372
|
+
config: {
|
|
1373
|
+
url: parsedUrl,
|
|
1374
|
+
headers: options.headers ? { ...options.headers } : void 0,
|
|
1375
|
+
...options.useSse ? { transport: "sse" } : {}
|
|
1376
|
+
},
|
|
1377
|
+
timeout: options.timeoutMs ?? 3e4,
|
|
1378
|
+
...oauthProvider ? {
|
|
1379
|
+
authProvider: oauthProvider
|
|
1380
|
+
} : {}
|
|
1381
|
+
});
|
|
1382
|
+
try {
|
|
1383
|
+
await client.connect();
|
|
1384
|
+
} catch (e) {
|
|
1385
|
+
oauthProvider?.dispose();
|
|
1386
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1387
|
+
if (/authentication required|unauthorized|401/i.test(msg)) {
|
|
1388
|
+
throw new Error(
|
|
1389
|
+
`Zoho MCP authentication failed for "${id}".
|
|
1390
|
+
\u2022 Ensure ZOHO_MCP_URL is the full Connect URL from https://mcp.zoho.com
|
|
1391
|
+
\u2022 Complete the browser OAuth login when prompted
|
|
1392
|
+
\u2022 Or set auth: "none" and pass Authorization headers if you use a pre-authorized URL
|
|
1393
|
+
Original: ${msg}`,
|
|
1394
|
+
{ cause: e instanceof Error ? e : void 0 }
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
throw e;
|
|
1398
|
+
}
|
|
1399
|
+
try {
|
|
1400
|
+
const toolBox = await mcpClientToToolBox(client, {
|
|
1401
|
+
namePrefix: id,
|
|
1402
|
+
logger: options.logger
|
|
1403
|
+
});
|
|
1404
|
+
const tools = await client.listTools();
|
|
1405
|
+
const prefixed = tools.map((t) => `${id}__${t.name}`);
|
|
1406
|
+
try {
|
|
1407
|
+
oauthProvider?.dispose();
|
|
1408
|
+
} catch {
|
|
1409
|
+
}
|
|
1410
|
+
let closed = false;
|
|
1411
|
+
return {
|
|
1412
|
+
id,
|
|
1413
|
+
toolBox,
|
|
1414
|
+
toolNames: prefixed,
|
|
1415
|
+
client,
|
|
1416
|
+
async close() {
|
|
1417
|
+
if (closed) return;
|
|
1418
|
+
closed = true;
|
|
1419
|
+
try {
|
|
1420
|
+
await client.disconnect();
|
|
1421
|
+
} catch (err) {
|
|
1422
|
+
options.logger?.error(`zoho close failed: ${err.message}`, err);
|
|
1423
|
+
} finally {
|
|
1424
|
+
try {
|
|
1425
|
+
oauthProvider?.dispose();
|
|
1426
|
+
} catch {
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
} catch (e) {
|
|
1432
|
+
oauthProvider?.dispose();
|
|
1433
|
+
try {
|
|
1434
|
+
await client.disconnect();
|
|
1435
|
+
} catch {
|
|
1436
|
+
}
|
|
1437
|
+
throw e;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// src/connectors-zoho/toolbox.ts
|
|
1442
|
+
async function zohoToolBoxFromClient(client, options) {
|
|
1443
|
+
return mcpClientToToolBox(client, {
|
|
1444
|
+
...options,
|
|
1445
|
+
namePrefix: options?.id ?? "zoho"
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
export {
|
|
1449
|
+
connectZohoMcp,
|
|
1450
|
+
zohoToolBoxFromClient
|
|
1451
|
+
};
|