github-to-mcp-monorepo 1.0.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/.env.example +8 -0
- package/.github/CODEOWNERS +6 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +4 -0
- package/ARCHITECTURE.md +1429 -0
- package/CHANGELOG.md +167 -0
- package/CONTRIBUTING.md +327 -0
- package/LICENSE +201 -0
- package/README.md +1028 -0
- package/SECURITY.md +248 -0
- package/VISUAL_GUIDE.md +437 -0
- package/apps/vscode/IMPLEMENTATION.md +480 -0
- package/apps/vscode/README.md +248 -0
- package/apps/vscode/package.json +381 -0
- package/apps/vscode/resources/icon.png +0 -0
- package/apps/vscode/resources/icon.svg +5 -0
- package/apps/vscode/src/commands/browseRegistry.ts +211 -0
- package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
- package/apps/vscode/src/commands/convert.ts +82 -0
- package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
- package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
- package/apps/vscode/src/commands/index.ts +121 -0
- package/apps/vscode/src/commands/validate.ts +197 -0
- package/apps/vscode/src/extension.ts +464 -0
- package/apps/vscode/src/global.d.ts +36 -0
- package/apps/vscode/src/test/extension.test.ts +73 -0
- package/apps/vscode/src/utils/file-generator.ts +529 -0
- package/apps/vscode/src/utils/github-api.ts +335 -0
- package/apps/vscode/src/utils/index.ts +29 -0
- package/apps/vscode/src/utils/mcp-config.ts +334 -0
- package/apps/vscode/src/utils/storage.ts +87 -0
- package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
- package/apps/vscode/src/views/OutputChannelView.ts +195 -0
- package/apps/vscode/src/views/StatusBarItem.ts +251 -0
- package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
- package/apps/vscode/src/views/historyProvider.ts +75 -0
- package/apps/vscode/src/views/index.ts +12 -0
- package/apps/vscode/src/views/resultsPanel.ts +330 -0
- package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
- package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
- package/apps/vscode/src/webviews/index.ts +9 -0
- package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
- package/apps/vscode/tsconfig.json +20 -0
- package/apps/web/PLAYGROUND_GUIDE.md +499 -0
- package/apps/web/README.md +505 -0
- package/apps/web/app/api/convert/route.ts +100 -0
- package/apps/web/app/api/convert/stream/route.ts +198 -0
- package/apps/web/app/api/deploy/route.ts +157 -0
- package/apps/web/app/api/edge/route.ts +308 -0
- package/apps/web/app/api/export-docker/route.ts +284 -0
- package/apps/web/app/api/generate-openapi/route.ts +119 -0
- package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
- package/apps/web/app/api/playground/connect/route.ts +143 -0
- package/apps/web/app/api/playground/disconnect/route.ts +78 -0
- package/apps/web/app/api/playground/execute/route.ts +135 -0
- package/apps/web/app/api/playground/sessions/route.ts +103 -0
- package/apps/web/app/api/playground/tools/route.ts +117 -0
- package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
- package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
- package/apps/web/app/api/playground/v2/health/route.ts +80 -0
- package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
- package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
- package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
- package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
- package/apps/web/app/api/stream/route.ts +232 -0
- package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
- package/apps/web/app/batch/page.tsx +37 -0
- package/apps/web/app/convert/page.tsx +269 -0
- package/apps/web/app/dashboard/page.tsx +380 -0
- package/apps/web/app/globals.css +622 -0
- package/apps/web/app/layout.tsx +120 -0
- package/apps/web/app/manifest.ts +31 -0
- package/apps/web/app/opengraph-image.tsx +112 -0
- package/apps/web/app/page.old.tsx +924 -0
- package/apps/web/app/page.tsx +77 -0
- package/apps/web/app/playground/page.tsx +306 -0
- package/apps/web/app/playground/v2/error.tsx +163 -0
- package/apps/web/app/playground/v2/layout.tsx +58 -0
- package/apps/web/app/playground/v2/loading.tsx +152 -0
- package/apps/web/app/playground/v2/page.tsx +644 -0
- package/apps/web/app/playground/v2/providers.tsx +214 -0
- package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
- package/apps/web/app/playground/v2/use-url-state.ts +296 -0
- package/apps/web/app/providers.tsx +22 -0
- package/apps/web/app/sitemap.ts +32 -0
- package/apps/web/app/twitter-image.tsx +112 -0
- package/apps/web/components/BranchSelector.tsx +401 -0
- package/apps/web/components/ClaudeConfigExport.tsx +226 -0
- package/apps/web/components/Features.tsx +84 -0
- package/apps/web/components/Footer.tsx +119 -0
- package/apps/web/components/GenerationProgress.tsx +248 -0
- package/apps/web/components/GithubUrlInput.tsx +483 -0
- package/apps/web/components/Header.tsx +175 -0
- package/apps/web/components/Hero.tsx +117 -0
- package/apps/web/components/HowItWorks.tsx +119 -0
- package/apps/web/components/InstallBanner.tsx +158 -0
- package/apps/web/components/Logo.tsx +116 -0
- package/apps/web/components/ParticleBackground.tsx +105 -0
- package/apps/web/components/Playground.tsx +472 -0
- package/apps/web/components/PlaygroundToolTester.tsx +410 -0
- package/apps/web/components/ProductCards.tsx +179 -0
- package/apps/web/components/SplitView.tsx +194 -0
- package/apps/web/components/ToolFilter.tsx +260 -0
- package/apps/web/components/ToolList.tsx +325 -0
- package/apps/web/components/batch/BatchConvert.tsx +785 -0
- package/apps/web/components/batch/index.ts +7 -0
- package/apps/web/components/convert/ConfigTabs.tsx +230 -0
- package/apps/web/components/convert/ConversionResult.tsx +482 -0
- package/apps/web/components/convert/InlinePlayground.tsx +259 -0
- package/apps/web/components/convert/LoadingSteps.tsx +311 -0
- package/apps/web/components/convert/OneClickInstall.tsx +224 -0
- package/apps/web/components/convert/ToolCard.tsx +189 -0
- package/apps/web/components/convert/TryInPlayground.tsx +242 -0
- package/apps/web/components/convert/index.ts +12 -0
- package/apps/web/components/deploy/DeployButton.tsx +369 -0
- package/apps/web/components/deploy/index.ts +7 -0
- package/apps/web/components/docker/DockerExport.tsx +690 -0
- package/apps/web/components/docker/index.ts +7 -0
- package/apps/web/components/install/OneClickInstall.tsx +676 -0
- package/apps/web/components/install/index.ts +7 -0
- package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
- package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
- package/apps/web/components/playground/EmptyStates.tsx +305 -0
- package/apps/web/components/playground/ExecutionLog.tsx +260 -0
- package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
- package/apps/web/components/playground/JsonViewer.tsx +388 -0
- package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
- package/apps/web/components/playground/PromptsPanel.tsx +385 -0
- package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
- package/apps/web/components/playground/SchemaForm.tsx +477 -0
- package/apps/web/components/playground/ServerStatus.tsx +151 -0
- package/apps/web/components/playground/ShareButton.tsx +239 -0
- package/apps/web/components/playground/ToolsPanel.tsx +309 -0
- package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
- package/apps/web/components/playground/index.ts +74 -0
- package/apps/web/components/playground/types.ts +202 -0
- package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
- package/apps/web/components/streaming/index.ts +7 -0
- package/apps/web/components/ui/badge.tsx +42 -0
- package/apps/web/components/ui/button.tsx +88 -0
- package/apps/web/components/ui/card.tsx +75 -0
- package/apps/web/components/ui/code-block.tsx +122 -0
- package/apps/web/components/ui/index.ts +12 -0
- package/apps/web/components/ui/input.tsx +55 -0
- package/apps/web/components/ui/tabs.tsx +61 -0
- package/apps/web/hooks/index.ts +85 -0
- package/apps/web/hooks/types.ts +1173 -0
- package/apps/web/hooks/use-conversion.ts +133 -0
- package/apps/web/hooks/use-execution-history.ts +376 -0
- package/apps/web/hooks/use-generation-progress.ts +147 -0
- package/apps/web/hooks/use-local-storage.ts +88 -0
- package/apps/web/hooks/use-mcp-client.ts +623 -0
- package/apps/web/hooks/use-mcp-connection.ts +500 -0
- package/apps/web/hooks/use-mcp-execution.ts +282 -0
- package/apps/web/hooks/use-mcp-prompts.ts +441 -0
- package/apps/web/hooks/use-mcp-resources.ts +430 -0
- package/apps/web/hooks/use-mcp-tools.ts +540 -0
- package/apps/web/hooks/use-playground-store.ts +299 -0
- package/apps/web/hooks/use-playground.ts +184 -0
- package/apps/web/hooks/use-streaming-conversion.ts +227 -0
- package/apps/web/hooks/useBatchConversion.ts +271 -0
- package/apps/web/hooks/useDockerConfig.ts +161 -0
- package/apps/web/hooks/usePlatformDetection.ts +80 -0
- package/apps/web/hooks/useStreaming.ts +199 -0
- package/apps/web/lib/api/errors.ts +386 -0
- package/apps/web/lib/api/index.ts +137 -0
- package/apps/web/lib/api/logger.ts +187 -0
- package/apps/web/lib/api/middleware.ts +364 -0
- package/apps/web/lib/api/openapi.ts +977 -0
- package/apps/web/lib/api/session-manager.ts +594 -0
- package/apps/web/lib/api/types.ts +433 -0
- package/apps/web/lib/api/validation.ts +523 -0
- package/apps/web/lib/constants.ts +114 -0
- package/apps/web/lib/mcp/client.ts +1137 -0
- package/apps/web/lib/mcp/events.ts +651 -0
- package/apps/web/lib/mcp/index.ts +347 -0
- package/apps/web/lib/mcp/logger.ts +428 -0
- package/apps/web/lib/mcp/metrics.ts +703 -0
- package/apps/web/lib/mcp/retry.ts +616 -0
- package/apps/web/lib/mcp/session-manager.ts +779 -0
- package/apps/web/lib/mcp/transports.ts +988 -0
- package/apps/web/lib/mcp/types.ts +594 -0
- package/apps/web/lib/mcp-client-enhanced.ts +871 -0
- package/apps/web/lib/mcp-client.ts +778 -0
- package/apps/web/lib/mcp-errors.ts +489 -0
- package/apps/web/lib/mcp-sandbox.ts +593 -0
- package/apps/web/lib/mcp-testing.ts +428 -0
- package/apps/web/lib/mcp-types.ts +448 -0
- package/apps/web/lib/playground-store.tsx +1147 -0
- package/apps/web/lib/utils.ts +439 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +23 -0
- package/apps/web/package.json +55 -0
- package/apps/web/postcss.config.js +6 -0
- package/apps/web/public/.well-known/ai-plugin.json +17 -0
- package/apps/web/public/logo.svg +6 -0
- package/apps/web/public/robots.txt +22 -0
- package/apps/web/public/schema.json +27 -0
- package/apps/web/tailwind.config.js +26 -0
- package/apps/web/tailwind.config.ts +123 -0
- package/apps/web/tsconfig.json +20 -0
- package/apps/web/types/deploy.ts +139 -0
- package/apps/web/types/index.ts +247 -0
- package/apps/web/vercel.json +39 -0
- package/eslint.config.mjs +23 -0
- package/llms.txt +102 -0
- package/mkdocs/docs/api/core.md +318 -0
- package/mkdocs/docs/api/index.md +128 -0
- package/mkdocs/docs/api/mcp-server.md +301 -0
- package/mkdocs/docs/api/openapi-parser.md +254 -0
- package/mkdocs/docs/assets/logo.svg +7 -0
- package/mkdocs/docs/changelog.md +118 -0
- package/mkdocs/docs/cli/generate.md +148 -0
- package/mkdocs/docs/cli/index.md +52 -0
- package/mkdocs/docs/cli/inspect.md +164 -0
- package/mkdocs/docs/cli/serve.md +136 -0
- package/mkdocs/docs/concepts/classification.md +254 -0
- package/mkdocs/docs/concepts/how-it-works.md +299 -0
- package/mkdocs/docs/concepts/index.md +77 -0
- package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
- package/mkdocs/docs/concepts/tool-types.md +382 -0
- package/mkdocs/docs/contributing/architecture.md +262 -0
- package/mkdocs/docs/contributing/development.md +245 -0
- package/mkdocs/docs/contributing/index.md +73 -0
- package/mkdocs/docs/contributing/testing.md +320 -0
- package/mkdocs/docs/getting-started/configuration.md +235 -0
- package/mkdocs/docs/getting-started/index.md +54 -0
- package/mkdocs/docs/getting-started/installation.md +145 -0
- package/mkdocs/docs/getting-started/quickstart.md +160 -0
- package/mkdocs/docs/guides/batch.md +375 -0
- package/mkdocs/docs/guides/claude-desktop.md +227 -0
- package/mkdocs/docs/guides/cursor.md +188 -0
- package/mkdocs/docs/guides/custom-tools.md +367 -0
- package/mkdocs/docs/guides/index.md +78 -0
- package/mkdocs/docs/guides/private-repos.md +221 -0
- package/mkdocs/docs/guides/vscode.md +247 -0
- package/mkdocs/docs/index.md +175 -0
- package/mkdocs/docs/reference/config.md +223 -0
- package/mkdocs/docs/reference/env.md +192 -0
- package/mkdocs/docs/reference/index.md +102 -0
- package/mkdocs/docs/reference/tools.md +309 -0
- package/mkdocs/docs/stylesheets/extra.css +231 -0
- package/mkdocs/mkdocs.yml +204 -0
- package/mkdocs/overrides/.gitkeep +1 -0
- package/mkdocs/overrides/main.html +7 -0
- package/mkdocs/python-deps.txt +7 -0
- package/mkdocs/vercel.json +11 -0
- package/package.json +63 -0
- package/packages/core/package.json +61 -0
- package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
- package/packages/core/src/__tests__/cli.test.ts +235 -0
- package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
- package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
- package/packages/core/src/__tests__/github-client.test.ts +390 -0
- package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
- package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
- package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
- package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
- package/packages/core/src/__tests__/plugins.test.ts +467 -0
- package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
- package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
- package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
- package/packages/core/src/__tests__/streaming.test.ts +251 -0
- package/packages/core/src/additional-extractors.ts +333 -0
- package/packages/core/src/cache/cache-interface.ts +179 -0
- package/packages/core/src/cache/index.ts +210 -0
- package/packages/core/src/cache/redis-cache.ts +291 -0
- package/packages/core/src/cache/upstash-cache.ts +379 -0
- package/packages/core/src/cache.ts +251 -0
- package/packages/core/src/cli.ts +822 -0
- package/packages/core/src/code-extractor.ts +696 -0
- package/packages/core/src/docker-generator.ts +470 -0
- package/packages/core/src/edge-compatible.ts +491 -0
- package/packages/core/src/extractors/go-extractor.ts +791 -0
- package/packages/core/src/extractors/index.ts +9 -0
- package/packages/core/src/extractors/java-extractor.ts +937 -0
- package/packages/core/src/extractors/rust-extractor.ts +744 -0
- package/packages/core/src/github-client.ts +319 -0
- package/packages/core/src/go-generator.ts +356 -0
- package/packages/core/src/graphql-extractor.ts +358 -0
- package/packages/core/src/index.ts +797 -0
- package/packages/core/src/langchain-exporter.ts +617 -0
- package/packages/core/src/language-parsers.ts +1114 -0
- package/packages/core/src/mcp-introspector.ts +279 -0
- package/packages/core/src/monorepo-detector.ts +378 -0
- package/packages/core/src/plugins/index.ts +370 -0
- package/packages/core/src/plugins/registry.ts +404 -0
- package/packages/core/src/plugins/types.ts +215 -0
- package/packages/core/src/providers/base-provider.ts +246 -0
- package/packages/core/src/providers/bitbucket-client.ts +464 -0
- package/packages/core/src/providers/gitlab-client.ts +388 -0
- package/packages/core/src/providers/index.ts +176 -0
- package/packages/core/src/python-generator.ts +260 -0
- package/packages/core/src/queue/index.ts +100 -0
- package/packages/core/src/queue/memory-queue.ts +445 -0
- package/packages/core/src/queue/redis-queue.ts +578 -0
- package/packages/core/src/queue/types.ts +251 -0
- package/packages/core/src/readme-extractor.ts +409 -0
- package/packages/core/src/schema-generator.ts +638 -0
- package/packages/core/src/streaming.ts +999 -0
- package/packages/core/src/types.ts +289 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/core/tsup.config.ts +25 -0
- package/packages/mcp-server/README.md +297 -0
- package/packages/mcp-server/package.json +55 -0
- package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
- package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
- package/packages/mcp-server/src/index.ts +1206 -0
- package/packages/mcp-server/src/prompts/index.ts +601 -0
- package/packages/mcp-server/src/tools/export-docker.ts +362 -0
- package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
- package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
- package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
- package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
- package/packages/mcp-server/tsconfig.json +12 -0
- package/packages/mcp-server/tsup.config.ts +14 -0
- package/packages/openapi-parser/package-lock.json +3028 -0
- package/packages/openapi-parser/package.json +41 -0
- package/packages/openapi-parser/src/analyzer.ts +700 -0
- package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
- package/packages/openapi-parser/src/cli.ts +302 -0
- package/packages/openapi-parser/src/generator.ts +570 -0
- package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
- package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
- package/packages/openapi-parser/src/generators/index.ts +200 -0
- package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
- package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
- package/packages/openapi-parser/src/generators/types.ts +298 -0
- package/packages/openapi-parser/src/graphql-parser.ts +462 -0
- package/packages/openapi-parser/src/grpc-parser.ts +649 -0
- package/packages/openapi-parser/src/har-parser.ts +723 -0
- package/packages/openapi-parser/src/index.ts +635 -0
- package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
- package/packages/openapi-parser/src/parser.ts +231 -0
- package/packages/openapi-parser/src/postman-parser.ts +611 -0
- package/packages/openapi-parser/src/ref-resolver.ts +313 -0
- package/packages/openapi-parser/src/transformer.ts +459 -0
- package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
- package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
- package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
- package/packages/openapi-parser/tests/parsers.test.ts +847 -0
- package/packages/openapi-parser/tsconfig.json +9 -0
- package/packages/openapi-parser/tsup.config.ts +11 -0
- package/packages/registry/package.json +59 -0
- package/packages/registry/src/cli.ts +456 -0
- package/packages/registry/src/index.ts +44 -0
- package/packages/registry/src/popular/github.json +47 -0
- package/packages/registry/src/popular/index.ts +55 -0
- package/packages/registry/src/popular/linear.json +42 -0
- package/packages/registry/src/popular/notion.json +42 -0
- package/packages/registry/src/popular/openai.json +40 -0
- package/packages/registry/src/popular/resend.json +38 -0
- package/packages/registry/src/popular/slack.json +42 -0
- package/packages/registry/src/popular/stripe.json +163 -0
- package/packages/registry/src/popular/supabase.json +42 -0
- package/packages/registry/src/popular/twilio.json +40 -0
- package/packages/registry/src/popular/vercel.json +40 -0
- package/packages/registry/src/registry.ts +492 -0
- package/packages/registry/src/storage.ts +334 -0
- package/packages/registry/src/types.ts +275 -0
- package/packages/registry/src/updater.ts +208 -0
- package/packages/registry/tsconfig.json +10 -0
- package/packages/registry/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/build-docs.sh +16 -0
- package/server.json +9 -0
- package/templates/Dockerfile.python.template +60 -0
- package/templates/Dockerfile.typescript.template +60 -0
- package/templates/docker-compose.template.yml +68 -0
- package/tests/fixtures/express-app/index.js +34 -0
- package/tests/fixtures/express-app/routes/posts.js +43 -0
- package/tests/fixtures/express-app/routes/users.js +58 -0
- package/tests/fixtures/fastapi-app/main.py +125 -0
- package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
- package/tests/fixtures/graphql/simple-schema.graphql +65 -0
- package/tests/fixtures/mocks/github-api-responses.json +63 -0
- package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
- package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
- package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
- package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
- package/tests/fixtures/openapi/petstore.yaml +179 -0
- package/tests/integration/langchain-export.test.ts +405 -0
- package/tests/integration/openapi-conversion.test.ts +221 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +32 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client Implementation
|
|
3
|
+
* Client-side MCP protocol handler for communicating with MCP servers
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Multiple transport support (HTTP, WebSocket, SSE, In-Memory)
|
|
7
|
+
* - Automatic retry with exponential backoff
|
|
8
|
+
* - Request queuing and rate limiting
|
|
9
|
+
* - Event emitter for notifications
|
|
10
|
+
* - Streaming support for long-running operations
|
|
11
|
+
* - Connection health monitoring
|
|
12
|
+
*
|
|
13
|
+
* @see https://spec.modelcontextprotocol.io/
|
|
14
|
+
* @author nich (x.com/nichxbt | github.com/nirholas)
|
|
15
|
+
* @copyright 2024-2026 nich (nirholas)
|
|
16
|
+
* @license MIT
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
JsonRpcRequest,
|
|
21
|
+
JsonRpcResponse,
|
|
22
|
+
JsonRpcNotification,
|
|
23
|
+
McpTool,
|
|
24
|
+
ListToolsResult,
|
|
25
|
+
CallToolParams,
|
|
26
|
+
CallToolResult,
|
|
27
|
+
ListResourcesResult,
|
|
28
|
+
ReadResourceResult,
|
|
29
|
+
ListPromptsResult,
|
|
30
|
+
GetPromptResult,
|
|
31
|
+
InitializeParams,
|
|
32
|
+
InitializeResult,
|
|
33
|
+
ServerCapabilities,
|
|
34
|
+
ServerInfo,
|
|
35
|
+
isJsonRpcError,
|
|
36
|
+
MCP_METHODS,
|
|
37
|
+
MCP_PROTOCOL_VERSION,
|
|
38
|
+
ToolContent,
|
|
39
|
+
} from './mcp-types';
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
McpError,
|
|
43
|
+
McpConnectionError,
|
|
44
|
+
McpTimeoutError,
|
|
45
|
+
McpConnectionClosedError,
|
|
46
|
+
McpServerNotInitializedError,
|
|
47
|
+
McpToolNotFoundError,
|
|
48
|
+
McpToolTimeoutError,
|
|
49
|
+
createErrorFromJsonRpc,
|
|
50
|
+
wrapError,
|
|
51
|
+
isRetryableError,
|
|
52
|
+
} from './mcp-errors';
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Client Options & Interfaces
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Options for creating an MCP client
|
|
60
|
+
*/
|
|
61
|
+
export interface McpClientOptions {
|
|
62
|
+
/** Server code or identifier (for reference) */
|
|
63
|
+
serverCode?: string;
|
|
64
|
+
/** Request timeout in milliseconds */
|
|
65
|
+
timeout?: number;
|
|
66
|
+
/** Default timeout for tool calls */
|
|
67
|
+
toolTimeout?: number;
|
|
68
|
+
/** Callback for server output/logs */
|
|
69
|
+
onOutput?: (data: string) => void;
|
|
70
|
+
/** Callback for errors */
|
|
71
|
+
onError?: (error: string) => void;
|
|
72
|
+
/** Callback for notifications from server */
|
|
73
|
+
onNotification?: (method: string, params?: Record<string, unknown>) => void;
|
|
74
|
+
/** Client name for identification */
|
|
75
|
+
clientName?: string;
|
|
76
|
+
/** Client version */
|
|
77
|
+
clientVersion?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Default client info - nich (x.com/nichxbt | github.com/nirholas) */
|
|
81
|
+
const _DEFAULT_CLIENT_META = {
|
|
82
|
+
name: 'github-to-mcp-client',
|
|
83
|
+
author: 'nich',
|
|
84
|
+
links: ['x.com/nichxbt', 'github.com/nirholas'],
|
|
85
|
+
} as const;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Connection state for the MCP client
|
|
89
|
+
*/
|
|
90
|
+
export type McpClientState =
|
|
91
|
+
| 'disconnected'
|
|
92
|
+
| 'connecting'
|
|
93
|
+
| 'initializing'
|
|
94
|
+
| 'ready'
|
|
95
|
+
| 'error'
|
|
96
|
+
| 'closed';
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* MCP Client interface
|
|
100
|
+
*/
|
|
101
|
+
export interface IMcpClient {
|
|
102
|
+
/** Current connection state */
|
|
103
|
+
readonly state: McpClientState;
|
|
104
|
+
/** Server capabilities (available after initialization) */
|
|
105
|
+
readonly capabilities: ServerCapabilities | null;
|
|
106
|
+
|
|
107
|
+
/** Connect and initialize the MCP server */
|
|
108
|
+
connect(): Promise<void>;
|
|
109
|
+
/** Disconnect from the MCP server */
|
|
110
|
+
disconnect(): Promise<void>;
|
|
111
|
+
|
|
112
|
+
/** List available tools */
|
|
113
|
+
listTools(): Promise<McpTool[]>;
|
|
114
|
+
/** Call a tool with arguments */
|
|
115
|
+
callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult>;
|
|
116
|
+
|
|
117
|
+
/** List available resources */
|
|
118
|
+
listResources(): Promise<ListResourcesResult>;
|
|
119
|
+
/** Read a resource by URI */
|
|
120
|
+
readResource(uri: string): Promise<ReadResourceResult>;
|
|
121
|
+
|
|
122
|
+
/** List available prompts */
|
|
123
|
+
listPrompts(): Promise<ListPromptsResult>;
|
|
124
|
+
/** Get a prompt by name with arguments */
|
|
125
|
+
getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Transport Interface
|
|
130
|
+
// ============================================================================
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Transport interface for MCP communication
|
|
134
|
+
* Allows different transport mechanisms (stdio, SSE, WebSocket, etc.)
|
|
135
|
+
*/
|
|
136
|
+
export interface IMcpTransport {
|
|
137
|
+
/** Send a request and wait for response */
|
|
138
|
+
send(request: JsonRpcRequest): Promise<JsonRpcResponse>;
|
|
139
|
+
/** Send a notification (no response expected) */
|
|
140
|
+
notify(notification: JsonRpcNotification): Promise<void>;
|
|
141
|
+
/** Start the transport */
|
|
142
|
+
start(): Promise<void>;
|
|
143
|
+
/** Stop the transport */
|
|
144
|
+
stop(): Promise<void>;
|
|
145
|
+
/** Set message handler for incoming notifications */
|
|
146
|
+
onMessage(handler: (message: JsonRpcNotification) => void): void;
|
|
147
|
+
/** Check if transport is connected */
|
|
148
|
+
isConnected(): boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// HTTP/SSE Transport Implementation
|
|
153
|
+
// ============================================================================
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* HTTP transport options
|
|
157
|
+
*/
|
|
158
|
+
export interface HttpTransportOptions {
|
|
159
|
+
/** Server endpoint URL */
|
|
160
|
+
endpoint: string;
|
|
161
|
+
/** Request headers */
|
|
162
|
+
headers?: Record<string, string>;
|
|
163
|
+
/** Request timeout */
|
|
164
|
+
timeout?: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* HTTP Transport for MCP communication
|
|
169
|
+
* Sends requests via HTTP POST and handles SSE for streaming
|
|
170
|
+
*/
|
|
171
|
+
export class HttpTransport implements IMcpTransport {
|
|
172
|
+
private endpoint: string;
|
|
173
|
+
private headers: Record<string, string>;
|
|
174
|
+
private timeout: number;
|
|
175
|
+
private connected: boolean = false;
|
|
176
|
+
private messageHandler?: (message: JsonRpcNotification) => void;
|
|
177
|
+
private eventSource?: EventSource;
|
|
178
|
+
|
|
179
|
+
constructor(options: HttpTransportOptions) {
|
|
180
|
+
this.endpoint = options.endpoint;
|
|
181
|
+
this.headers = options.headers ?? {};
|
|
182
|
+
this.timeout = options.timeout ?? 30000;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async start(): Promise<void> {
|
|
186
|
+
this.connected = true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async stop(): Promise<void> {
|
|
190
|
+
this.connected = false;
|
|
191
|
+
if (this.eventSource) {
|
|
192
|
+
this.eventSource.close();
|
|
193
|
+
this.eventSource = undefined;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
isConnected(): boolean {
|
|
198
|
+
return this.connected;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
onMessage(handler: (message: JsonRpcNotification) => void): void {
|
|
202
|
+
this.messageHandler = handler;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async send(request: JsonRpcRequest): Promise<JsonRpcResponse> {
|
|
206
|
+
if (!this.connected) {
|
|
207
|
+
throw new McpConnectionClosedError('Transport is not connected');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const controller = new AbortController();
|
|
211
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const response = await fetch(this.endpoint, {
|
|
215
|
+
method: 'POST',
|
|
216
|
+
headers: {
|
|
217
|
+
'Content-Type': 'application/json',
|
|
218
|
+
...this.headers,
|
|
219
|
+
},
|
|
220
|
+
body: JSON.stringify(request),
|
|
221
|
+
signal: controller.signal,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (!response.ok) {
|
|
225
|
+
throw new McpConnectionError(
|
|
226
|
+
`HTTP error: ${response.status} ${response.statusText}`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const data = await response.json();
|
|
231
|
+
return data as JsonRpcResponse;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
234
|
+
throw new McpTimeoutError('Request timed out', this.timeout);
|
|
235
|
+
}
|
|
236
|
+
throw wrapError(error, 'Failed to send request');
|
|
237
|
+
} finally {
|
|
238
|
+
clearTimeout(timeoutId);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async notify(notification: JsonRpcNotification): Promise<void> {
|
|
243
|
+
if (!this.connected) {
|
|
244
|
+
throw new McpConnectionClosedError('Transport is not connected');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
await fetch(this.endpoint, {
|
|
249
|
+
method: 'POST',
|
|
250
|
+
headers: {
|
|
251
|
+
'Content-Type': 'application/json',
|
|
252
|
+
...this.headers,
|
|
253
|
+
},
|
|
254
|
+
body: JSON.stringify(notification),
|
|
255
|
+
});
|
|
256
|
+
} catch (error) {
|
|
257
|
+
// Notifications don't expect responses, so we swallow errors
|
|
258
|
+
console.warn('Failed to send notification:', error);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Connect to SSE endpoint for streaming notifications
|
|
264
|
+
*/
|
|
265
|
+
connectSSE(sseEndpoint: string): void {
|
|
266
|
+
if (this.eventSource) {
|
|
267
|
+
this.eventSource.close();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.eventSource = new EventSource(sseEndpoint);
|
|
271
|
+
|
|
272
|
+
this.eventSource.onmessage = (event) => {
|
|
273
|
+
try {
|
|
274
|
+
const message = JSON.parse(event.data) as JsonRpcNotification;
|
|
275
|
+
if (this.messageHandler && !('id' in message)) {
|
|
276
|
+
this.messageHandler(message);
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.warn('Failed to parse SSE message:', error);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
this.eventSource.onerror = () => {
|
|
284
|
+
console.warn('SSE connection error');
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ============================================================================
|
|
290
|
+
// In-Memory Transport for Testing
|
|
291
|
+
// ============================================================================
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Handler function type for in-memory transport
|
|
295
|
+
*/
|
|
296
|
+
export type InMemoryHandler = (request: JsonRpcRequest) => Promise<JsonRpcResponse>;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* In-memory transport for testing MCP clients
|
|
300
|
+
*/
|
|
301
|
+
export class InMemoryTransport implements IMcpTransport {
|
|
302
|
+
private handler: InMemoryHandler;
|
|
303
|
+
private connected: boolean = false;
|
|
304
|
+
private messageHandler?: (message: JsonRpcNotification) => void;
|
|
305
|
+
|
|
306
|
+
constructor(handler: InMemoryHandler) {
|
|
307
|
+
this.handler = handler;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async start(): Promise<void> {
|
|
311
|
+
this.connected = true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async stop(): Promise<void> {
|
|
315
|
+
this.connected = false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
isConnected(): boolean {
|
|
319
|
+
return this.connected;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
onMessage(handler: (message: JsonRpcNotification) => void): void {
|
|
323
|
+
this.messageHandler = handler;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async send(request: JsonRpcRequest): Promise<JsonRpcResponse> {
|
|
327
|
+
if (!this.connected) {
|
|
328
|
+
throw new McpConnectionClosedError('Transport is not connected');
|
|
329
|
+
}
|
|
330
|
+
return this.handler(request);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async notify(): Promise<void> {
|
|
334
|
+
// No-op for in-memory transport
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Simulate a notification from the server
|
|
339
|
+
*/
|
|
340
|
+
simulateNotification(notification: JsonRpcNotification): void {
|
|
341
|
+
if (this.messageHandler) {
|
|
342
|
+
this.messageHandler(notification);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ============================================================================
|
|
348
|
+
// MCP Client Implementation
|
|
349
|
+
// ============================================================================
|
|
350
|
+
|
|
351
|
+
const DEFAULT_OPTIONS: Required<Pick<McpClientOptions, 'timeout' | 'toolTimeout' | 'clientName' | 'clientVersion'>> = {
|
|
352
|
+
timeout: 30000,
|
|
353
|
+
toolTimeout: 60000,
|
|
354
|
+
clientName: 'github-to-mcp-client',
|
|
355
|
+
clientVersion: '1.0.0',
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* MCP Client implementation
|
|
360
|
+
*/
|
|
361
|
+
export class McpClient implements IMcpClient {
|
|
362
|
+
private transport: IMcpTransport;
|
|
363
|
+
private options: McpClientOptions & typeof DEFAULT_OPTIONS;
|
|
364
|
+
private _state: McpClientState = 'disconnected';
|
|
365
|
+
private _capabilities: ServerCapabilities | null = null;
|
|
366
|
+
private requestId: number = 0;
|
|
367
|
+
private cachedTools: McpTool[] | null = null;
|
|
368
|
+
|
|
369
|
+
constructor(transport: IMcpTransport, options: McpClientOptions = {}) {
|
|
370
|
+
this.transport = transport;
|
|
371
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
372
|
+
|
|
373
|
+
// Set up notification handler
|
|
374
|
+
this.transport.onMessage((notification) => {
|
|
375
|
+
this.handleNotification(notification);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
get state(): McpClientState {
|
|
380
|
+
return this._state;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
get capabilities(): ServerCapabilities | null {
|
|
384
|
+
return this._capabilities;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ============================================================================
|
|
388
|
+
// Connection Management
|
|
389
|
+
// ============================================================================
|
|
390
|
+
|
|
391
|
+
async connect(): Promise<void> {
|
|
392
|
+
if (this._state === 'ready') {
|
|
393
|
+
return; // Already connected
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (this._state === 'connecting' || this._state === 'initializing') {
|
|
397
|
+
throw new McpError('Connection already in progress', -32001);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
try {
|
|
401
|
+
this._state = 'connecting';
|
|
402
|
+
this.options.onOutput?.('Connecting to MCP server...');
|
|
403
|
+
|
|
404
|
+
// Start the transport
|
|
405
|
+
await this.transport.start();
|
|
406
|
+
|
|
407
|
+
this._state = 'initializing';
|
|
408
|
+
this.options.onOutput?.('Initializing MCP session...');
|
|
409
|
+
|
|
410
|
+
// Initialize the MCP session
|
|
411
|
+
const initParams: InitializeParams = {
|
|
412
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
413
|
+
capabilities: {
|
|
414
|
+
roots: { listChanged: true },
|
|
415
|
+
},
|
|
416
|
+
clientInfo: {
|
|
417
|
+
name: this.options.clientName,
|
|
418
|
+
version: this.options.clientVersion,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const initResult = await this.request<InitializeResult>(
|
|
423
|
+
MCP_METHODS.INITIALIZE,
|
|
424
|
+
initParams
|
|
425
|
+
);
|
|
426
|
+
|
|
427
|
+
this._capabilities = initResult.capabilities;
|
|
428
|
+
|
|
429
|
+
// Send initialized notification
|
|
430
|
+
await this.notify(MCP_METHODS.INITIALIZED);
|
|
431
|
+
|
|
432
|
+
this._state = 'ready';
|
|
433
|
+
this.options.onOutput?.(
|
|
434
|
+
`Connected to ${initResult.serverInfo.name} v${initResult.serverInfo.version}`
|
|
435
|
+
);
|
|
436
|
+
} catch (error) {
|
|
437
|
+
this._state = 'error';
|
|
438
|
+
const wrappedError = wrapError(error, 'Failed to connect to MCP server');
|
|
439
|
+
this.options.onError?.(wrappedError.message);
|
|
440
|
+
throw wrappedError;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async disconnect(): Promise<void> {
|
|
445
|
+
if (this._state === 'disconnected' || this._state === 'closed') {
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
// Send shutdown request if connected
|
|
451
|
+
if (this._state === 'ready') {
|
|
452
|
+
try {
|
|
453
|
+
await this.request(MCP_METHODS.SHUTDOWN, {});
|
|
454
|
+
} catch {
|
|
455
|
+
// Ignore shutdown errors
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
await this.transport.stop();
|
|
460
|
+
this._state = 'closed';
|
|
461
|
+
this._capabilities = null;
|
|
462
|
+
this.cachedTools = null;
|
|
463
|
+
this.options.onOutput?.('Disconnected from MCP server');
|
|
464
|
+
} catch (error) {
|
|
465
|
+
this._state = 'error';
|
|
466
|
+
throw wrapError(error, 'Failed to disconnect from MCP server');
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ============================================================================
|
|
471
|
+
// Tool Operations
|
|
472
|
+
// ============================================================================
|
|
473
|
+
|
|
474
|
+
async listTools(): Promise<McpTool[]> {
|
|
475
|
+
this.ensureReady();
|
|
476
|
+
|
|
477
|
+
// Return cached tools if available and caching is enabled
|
|
478
|
+
if (this.cachedTools) {
|
|
479
|
+
return this.cachedTools;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const result = await this.request<ListToolsResult>(MCP_METHODS.TOOLS_LIST);
|
|
483
|
+
this.cachedTools = result.tools;
|
|
484
|
+
return result.tools;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult> {
|
|
488
|
+
this.ensureReady();
|
|
489
|
+
|
|
490
|
+
// Verify tool exists
|
|
491
|
+
const tools = await this.listTools();
|
|
492
|
+
const tool = tools.find((t) => t.name === name);
|
|
493
|
+
if (!tool) {
|
|
494
|
+
throw new McpToolNotFoundError(name);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const params: CallToolParams = {
|
|
498
|
+
name,
|
|
499
|
+
arguments: args,
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
const result = await this.request<CallToolResult>(
|
|
504
|
+
MCP_METHODS.TOOLS_CALL,
|
|
505
|
+
params,
|
|
506
|
+
this.options.toolTimeout
|
|
507
|
+
);
|
|
508
|
+
return result;
|
|
509
|
+
} catch (error) {
|
|
510
|
+
if (error instanceof McpTimeoutError) {
|
|
511
|
+
throw new McpToolTimeoutError(name, this.options.toolTimeout);
|
|
512
|
+
}
|
|
513
|
+
throw error;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// ============================================================================
|
|
518
|
+
// Resource Operations
|
|
519
|
+
// ============================================================================
|
|
520
|
+
|
|
521
|
+
async listResources(): Promise<ListResourcesResult> {
|
|
522
|
+
this.ensureReady();
|
|
523
|
+
return this.request<ListResourcesResult>(MCP_METHODS.RESOURCES_LIST);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async readResource(uri: string): Promise<ReadResourceResult> {
|
|
527
|
+
this.ensureReady();
|
|
528
|
+
return this.request<ReadResourceResult>(MCP_METHODS.RESOURCES_READ, { uri });
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// ============================================================================
|
|
532
|
+
// Prompt Operations
|
|
533
|
+
// ============================================================================
|
|
534
|
+
|
|
535
|
+
async listPrompts(): Promise<ListPromptsResult> {
|
|
536
|
+
this.ensureReady();
|
|
537
|
+
return this.request<ListPromptsResult>(MCP_METHODS.PROMPTS_LIST);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult> {
|
|
541
|
+
this.ensureReady();
|
|
542
|
+
return this.request<GetPromptResult>(MCP_METHODS.PROMPTS_GET, { name, arguments: args });
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ============================================================================
|
|
546
|
+
// Low-level Request/Notification Methods
|
|
547
|
+
// ============================================================================
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Send a JSON-RPC request and wait for response
|
|
551
|
+
*/
|
|
552
|
+
async request<T>(
|
|
553
|
+
method: string,
|
|
554
|
+
params?: object,
|
|
555
|
+
timeout?: number
|
|
556
|
+
): Promise<T> {
|
|
557
|
+
const request: JsonRpcRequest = {
|
|
558
|
+
jsonrpc: '2.0',
|
|
559
|
+
id: ++this.requestId,
|
|
560
|
+
method,
|
|
561
|
+
...(params && { params: params as Record<string, unknown> }),
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// Apply timeout if specified
|
|
565
|
+
const effectiveTimeout = timeout ?? this.options.timeout;
|
|
566
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
567
|
+
setTimeout(
|
|
568
|
+
() => reject(new McpTimeoutError(`Request timed out: ${method}`, effectiveTimeout)),
|
|
569
|
+
effectiveTimeout
|
|
570
|
+
);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const responsePromise = this.transport.send(request);
|
|
574
|
+
|
|
575
|
+
const response = await Promise.race([responsePromise, timeoutPromise]);
|
|
576
|
+
|
|
577
|
+
if (isJsonRpcError(response)) {
|
|
578
|
+
throw createErrorFromJsonRpc(response.error);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if ('result' in response) {
|
|
582
|
+
return response.result as T;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
throw new McpError('Invalid response format', -32600);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Send a JSON-RPC notification (no response expected)
|
|
590
|
+
*/
|
|
591
|
+
async notify(method: string, params?: Record<string, unknown>): Promise<void> {
|
|
592
|
+
const notification: JsonRpcNotification = {
|
|
593
|
+
jsonrpc: '2.0',
|
|
594
|
+
method,
|
|
595
|
+
...(params && { params }),
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
await this.transport.notify(notification);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ============================================================================
|
|
602
|
+
// Private Methods
|
|
603
|
+
// ============================================================================
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Ensure client is in ready state
|
|
607
|
+
*/
|
|
608
|
+
private ensureReady(): void {
|
|
609
|
+
if (this._state !== 'ready') {
|
|
610
|
+
throw new McpServerNotInitializedError(
|
|
611
|
+
`Client is not ready. Current state: ${this._state}`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Handle incoming notifications from server
|
|
618
|
+
*/
|
|
619
|
+
private handleNotification(notification: JsonRpcNotification): void {
|
|
620
|
+
const { method, params } = notification;
|
|
621
|
+
|
|
622
|
+
// Invalidate cache on relevant notifications
|
|
623
|
+
if (method === MCP_METHODS.NOTIFICATION_TOOLS_LIST_CHANGED) {
|
|
624
|
+
this.cachedTools = null;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Call user's notification handler
|
|
628
|
+
this.options.onNotification?.(method, params);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// ============================================================================
|
|
633
|
+
// Factory Functions
|
|
634
|
+
// ============================================================================
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Create an MCP client with HTTP transport
|
|
638
|
+
*/
|
|
639
|
+
export function createHttpClient(
|
|
640
|
+
endpoint: string,
|
|
641
|
+
options?: McpClientOptions & { headers?: Record<string, string> }
|
|
642
|
+
): McpClient {
|
|
643
|
+
const transport = new HttpTransport({
|
|
644
|
+
endpoint,
|
|
645
|
+
headers: options?.headers,
|
|
646
|
+
timeout: options?.timeout,
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
return new McpClient(transport, options);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Create an MCP client with in-memory transport for testing
|
|
654
|
+
*/
|
|
655
|
+
export function createTestClient(
|
|
656
|
+
handler: InMemoryHandler,
|
|
657
|
+
options?: McpClientOptions
|
|
658
|
+
): { client: McpClient; transport: InMemoryTransport } {
|
|
659
|
+
const transport = new InMemoryTransport(handler);
|
|
660
|
+
const client = new McpClient(transport, options);
|
|
661
|
+
return { client, transport };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// ============================================================================
|
|
665
|
+
// Helper Functions
|
|
666
|
+
// ============================================================================
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Extract text content from tool result
|
|
670
|
+
*/
|
|
671
|
+
export function extractTextContent(result: CallToolResult): string {
|
|
672
|
+
return result.content
|
|
673
|
+
.filter((c) => c.type === 'text')
|
|
674
|
+
.map((c) => (c as { type: 'text'; text: string }).text)
|
|
675
|
+
.join('\n');
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Check if tool result contains an error
|
|
680
|
+
*/
|
|
681
|
+
export function isToolResultError(result: CallToolResult): boolean {
|
|
682
|
+
return result.isError === true;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Build tool arguments from schema and values
|
|
687
|
+
*/
|
|
688
|
+
export function buildToolArguments(
|
|
689
|
+
tool: McpTool,
|
|
690
|
+
values: Record<string, unknown>
|
|
691
|
+
): Record<string, unknown> {
|
|
692
|
+
const args: Record<string, unknown> = {};
|
|
693
|
+
const schema = tool.inputSchema;
|
|
694
|
+
|
|
695
|
+
if (schema.properties) {
|
|
696
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
697
|
+
if (key in values) {
|
|
698
|
+
args[key] = values[key];
|
|
699
|
+
} else if ('default' in prop) {
|
|
700
|
+
args[key] = prop.default;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return args;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Validate tool arguments against schema
|
|
710
|
+
*/
|
|
711
|
+
export function validateToolArguments(
|
|
712
|
+
tool: McpTool,
|
|
713
|
+
args: Record<string, unknown>
|
|
714
|
+
): string[] {
|
|
715
|
+
const errors: string[] = [];
|
|
716
|
+
const schema = tool.inputSchema;
|
|
717
|
+
|
|
718
|
+
// Check required fields
|
|
719
|
+
if (schema.required) {
|
|
720
|
+
for (const required of schema.required) {
|
|
721
|
+
if (!(required in args) || args[required] === undefined) {
|
|
722
|
+
errors.push(`Missing required argument: ${required}`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Check property types (basic validation)
|
|
728
|
+
if (schema.properties) {
|
|
729
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
730
|
+
if (key in args && args[key] !== undefined) {
|
|
731
|
+
const value = args[key];
|
|
732
|
+
const expectedType = prop.type;
|
|
733
|
+
|
|
734
|
+
if (expectedType === 'string' && typeof value !== 'string') {
|
|
735
|
+
errors.push(`Argument '${key}' must be a string`);
|
|
736
|
+
} else if (expectedType === 'number' && typeof value !== 'number') {
|
|
737
|
+
errors.push(`Argument '${key}' must be a number`);
|
|
738
|
+
} else if (expectedType === 'boolean' && typeof value !== 'boolean') {
|
|
739
|
+
errors.push(`Argument '${key}' must be a boolean`);
|
|
740
|
+
} else if (expectedType === 'array' && !Array.isArray(value)) {
|
|
741
|
+
errors.push(`Argument '${key}' must be an array`);
|
|
742
|
+
} else if (expectedType === 'object' && (typeof value !== 'object' || value === null)) {
|
|
743
|
+
errors.push(`Argument '${key}' must be an object`);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Check enum values
|
|
747
|
+
if (prop.enum && !prop.enum.includes(value as string | number | boolean)) {
|
|
748
|
+
errors.push(`Argument '${key}' must be one of: ${prop.enum.join(', ')}`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return errors;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// ============================================================================
|
|
758
|
+
// Re-exports
|
|
759
|
+
// ============================================================================
|
|
760
|
+
|
|
761
|
+
export type {
|
|
762
|
+
McpTool,
|
|
763
|
+
CallToolResult,
|
|
764
|
+
ListToolsResult,
|
|
765
|
+
ListResourcesResult,
|
|
766
|
+
ReadResourceResult,
|
|
767
|
+
ListPromptsResult,
|
|
768
|
+
GetPromptResult,
|
|
769
|
+
ServerCapabilities,
|
|
770
|
+
} from './mcp-types';
|
|
771
|
+
|
|
772
|
+
export {
|
|
773
|
+
McpError,
|
|
774
|
+
McpConnectionError,
|
|
775
|
+
McpTimeoutError,
|
|
776
|
+
McpToolNotFoundError,
|
|
777
|
+
McpToolTimeoutError,
|
|
778
|
+
} from './mcp-errors';
|