diff --git a/CHANGELOG.md b/CHANGELOG.md index 4381d72..ffd08b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,19 +3,19 @@ ## [Unreleased] ### CLI -- `mcporter config add` now accepts plural `--args` as an alias for repeated stdio arguments, matching common CLI muscle memory. (PR #93, thanks @Jah-yee) +- Preserve OAuth flow vs post-auth transport failures so invalid OAuth/provider errors surface directly, while real legacy 404/405 transport mismatches still fall back to SSE correctly. (PR #97, thanks @mavam) +- Ignore static `Authorization` headers once OAuth is active so imported editor configs cannot override fresh OAuth tokens. (PR #123, thanks @ahonn) +- Keep `mcporter call --output json` parseable by emitting valid JSON even when the command falls back to raw output. (PR #128, thanks @armanddp) +- Render `resource` content blocks in call output helpers instead of dropping them, including markdown resources and JSON text payloads. (PR #124, thanks @mvanhorn) +- Preserve full JSON/error payloads when `data` is just one field instead of collapsing the response to `data` alone. (PR #106, thanks @AielloChan) - Generated CLIs now parse object-valued flags as JSON and render object placeholders/examples with JSON-shaped help text, so tools like Jira `fields` no longer receive raw strings. (PR #114, thanks @v2nic) - Deduplicate concurrent keep-alive daemon restarts per server so repeated fatal errors only force-close the cached daemon transport once before retrying. (PR #125, thanks @zm2231) -- Keep `mcporter call --output json` parseable by emitting valid JSON even when the command falls back to raw output. (PR #128, thanks @armanddp) -- Ignore static `Authorization` headers once OAuth is active so imported editor configs cannot override fresh OAuth tokens. (PR #123, thanks @ahonn) -- Preserve full JSON/error payloads when `data` is just one field instead of collapsing the response to `data` alone. (PR #106, thanks @AielloChan) -- Render `resource` content blocks in call output helpers instead of dropping them, including markdown resources and JSON text payloads. (PR #124, thanks @mvanhorn) -- Preserve OAuth flow vs post-auth transport failures so invalid OAuth/provider errors surface directly, while real legacy 404/405 transport mismatches still fall back to SSE correctly. (PR #97, thanks @mavam) +- `mcporter config add` now accepts plural `--args` as an alias for repeated stdio arguments, matching common CLI muscle memory. (PR #93, thanks @Jah-yee) - Preserve default imports when `mcporter config add` writes a config file, instead of forcing `"imports": []`. - OAuth: avoid crashing on headless Linux when `xdg-open` is unavailable; clear stale dynamic-port client registrations; close callback server if stale-client persistence reads fail. (PR #72, thanks @mgonto) - Added optional `oauthScope`/`oauth_scope` config override as an escape hatch for providers that require explicit scopes. -- `createCallResult().json()` now collects all parseable JSON entries from MCP content arrays (single item stays backward-compatible), and raw inspect depth now stays readable without unbounded traversal. (PR #91, thanks @Blankdlh) - OAuth wait/redirect now share one deferred to eliminate authorization race windows and preserve stable close-path errors, including wait-before-redirect and repeated-redirect flows. (PR #70, thanks @monotykamary) +- `createCallResult().json()` now collects all parseable JSON entries from MCP content arrays (single item stays backward-compatible), and raw inspect depth now stays readable without unbounded traversal. (PR #91, thanks @Blankdlh) - Added `--raw-strings` (numeric coercion off) and `--no-coerce` (all coercion off) for `mcporter call` argument parsing so IDs/codes can stay literal strings. (PR #59, thanks @nobrainer-tech) - Added `CallResult.images()` plus opt-in `mcporter call --save-images ` so image content blocks can be persisted without changing existing stdout output contracts. (PR #61, thanks @daniella-11ways) - OAuth transport retries now classify HTTP 405 as HTTP (not auth) and OAuth promotion applies to configured HTTP servers too, so post-auth fallback flows no longer drop credentials on 405-only endpoints. (PR #48, thanks @caseyg) diff --git a/docs/livetests.md b/docs/livetests.md index 46862a2..c6763e8 100644 --- a/docs/livetests.md +++ b/docs/livetests.md @@ -20,12 +20,15 @@ MCP_LIVE_TESTS=1 pnpm test:live This runs the Vitest suite under `tests/live`, in-band, with longer timeouts. ## Current coverage -- **DeepWiki** (both wire protocols): - - Streamable HTTP: `https://mcp.deepwiki.com/mcp` - - SSE: `https://mcp.deepwiki.com/sse` - - Test: calls `read_wiki_structure repoName:facebook/react` and asserts a non-empty result. +- **DeepWiki**: + - Streamable HTTP success path: `https://mcp.deepwiki.com/mcp` + - Deprecated SSE endpoint classification: `https://mcp.deepwiki.com/sse` + - Tests: + - call `read_wiki_structure repoName:facebook/react` and assert a non-empty result over Streamable HTTP + - assert the legacy SSE endpoint currently returns a structured HTTP `410` issue envelope ## Notes - Tests are skipped entirely unless `MCP_LIVE_TESTS=1` is set. - Ensure network egress is allowed. No secrets are required for the current DeepWiki checks. +- As of 2026-03-29, DeepWiki's hosted `/sse` endpoint responds with HTTP `410`, so the live suite treats that as a compatibility/error-classification smoke rather than a success-path transport check. - Keep assertions minimal to reduce flake; these are availability smokes, not full contract tests. diff --git a/tests/live/deepwiki-live.test.ts b/tests/live/deepwiki-live.test.ts index 214842b..8579e41 100644 --- a/tests/live/deepwiki-live.test.ts +++ b/tests/live/deepwiki-live.test.ts @@ -3,10 +3,8 @@ import { promisify } from 'node:util'; import { describe, expect, it } from 'vitest'; const LIVE_FLAG = process.env.MCP_LIVE_TESTS === '1'; -const ENDPOINTS = [ - { name: 'streamable-http', url: 'https://mcp.deepwiki.com/mcp' }, - { name: 'sse', url: 'https://mcp.deepwiki.com/sse' }, -]; +const STREAMABLE_HTTP_URL = 'https://mcp.deepwiki.com/mcp'; +const SSE_URL = 'https://mcp.deepwiki.com/sse'; const execFileAsync = promisify(execFile); @@ -18,36 +16,47 @@ function skipReason(): string | undefined { } describe.skipIf(Boolean(skipReason()))('deepwiki live', () => { - ENDPOINTS.forEach(({ name, url }) => { - it(`lists wiki structure via ${name}`, async () => { - const { stdout, stderr } = await execFileAsync('node', [ - 'dist/cli.js', - 'call', - url, - 'read_wiki_structure', - 'repoName:facebook/react', - '--output', - 'json', - ]); - const normalized = stdout.trim() || stderr.trim(); - // Response comes back as a JS-object literal string; just assert it contains the section list. - expect(normalized).toContain('Available pages for facebook/react'); - expect(normalized).toContain('Overview'); - }, 30_000); + it('lists wiki structure via streamable-http', async () => { + const { stdout, stderr } = await execFileAsync('node', [ + 'dist/cli.js', + 'call', + STREAMABLE_HTTP_URL, + 'read_wiki_structure', + 'repoName:facebook/react', + '--output', + 'json', + ]); + const normalized = stdout.trim() || stderr.trim(); + expect(normalized).toContain('Available pages for facebook/react'); + expect(normalized).toContain('Overview'); + }, 30_000); - it(`prints plain text when default output is used via ${name}`, async () => { - const { stdout, stderr } = await execFileAsync('node', [ - 'dist/cli.js', - 'call', - url, - 'read_wiki_structure', - 'repoName:facebook/react', - ]); - const normalized = (stdout || stderr).trim(); - expect(normalized).toContain('Available pages for facebook/react'); - // Ensure we rendered the text content, not the JSON envelope. - expect(normalized).not.toContain('"type"'); - expect(normalized.startsWith('{')).toBe(false); - }, 30_000); - }); + it('prints the readable result when default output is used via streamable-http', async () => { + const { stdout, stderr } = await execFileAsync('node', [ + 'dist/cli.js', + 'call', + STREAMABLE_HTTP_URL, + 'read_wiki_structure', + 'repoName:facebook/react', + ]); + const normalized = (stdout || stderr).trim(); + expect(normalized).toContain('Available pages for facebook/react'); + expect(normalized).toContain('Overview'); + expect(normalized).not.toContain('"type"'); + }, 30_000); + + it('reports the deprecated sse endpoint as a structured 410 issue', async () => { + const { stdout, stderr } = await execFileAsync('node', [ + 'dist/cli.js', + 'call', + SSE_URL, + 'read_wiki_structure', + 'repoName:facebook/react', + '--output', + 'json', + ]); + const normalized = stdout.trim() || stderr.trim(); + expect(normalized).toContain('"statusCode": 410'); + expect(normalized).toContain('"kind": "http"'); + }, 30_000); });