Note: This contains future work (P1/P2/P3).
- P3: Optional
- P2: Nice-to-Have
- 2. Export Profile Command
- 3. OpenAPI Operation Filter for Default Profile
- 4. Harden query parameter redaction canonicalization
- 7. Strengthen ReDoS Protection in Regex Compiler
- 8. Limit HTTP profile server cache growth
- 9. Break MCPServer-HttpTransport circular dependency
- 10. Split HttpTransport into smaller modules
- 11. Reduce usage of any casts in HTTP transport
- 12. Avoid HTTP profile hint collisions across shared IPs
- 13. Prevent unbounded growth of HTTP profile hint cache
- 14. Consider strict OAuth redirect scheme allowlist
- 15. Replace JSON fingerprint auth comparison for tenant collisions
- 16. Extract tenant session lifecycle from HttpTransport
Goal: Allow exporting auto-generated profile to file/stdout instead of using it directly.
Use cases:
- Generate starter profile for manual customization
- Debug auto-generation logic
- Version control profile alongside OpenAPI spec
- Share profiles between team members
Implementation:
# Export to file
mcp4openapi export-profile \
--openapi-spec-path=api.yaml \
--mcp-profile-path=profile.json \
--mcp-toolname-strategy=balanced \
--mcp-toolname-max=45 \
--mcp-toolname-min-parts=3 \
--mcp-toolname-min-length=20
# Export to stdout (for piping)
mcp4openapi export-profile --openapi-spec-path=api.yamlTechnical approach:
- Reuse existing
ProfileLoader.createDefaultProfile()- no duplication! - Add CLI command parser (yargs or commander)
- Add
src/cli-export.tsfor export logic - Support all naming strategies and options
- Format JSON with 2-space indent
Files to modify:
src/cli-export.ts(new) - export command implementationsrc/index.ts- add command routingpackage.json- add bin commandmcp4openapi-exportREADME.md- document export command
Estimated effort: 1-2 hours (mostly CLI parsing and formatting)
Note: This command could include auto-detection of probe endpoints for token validation in future versions.
Current: Without profile, all OpenAPI operations generate tools. Complex APIs may produce 100+ tools with parameter inflation warnings.
Goal: Allow filtering operations when auto-generating default profile.
Implementation Options:
Option A: Whitelist (Simple, Recommended for Start)
export DEFAULT_PROFILE_ALLOWED_OPERATIONS="getProject,listProjects,createIssue"Pros: Simple, deterministic, easy to audit Cons: Requires maintenance when API changes
Option A2: Regex Whitelist (Flexible)
export DEFAULT_PROFILE_OPERATIONS_REGEX="^(get|list|create)"
# Example: only read operations
export DEFAULT_PROFILE_OPERATIONS_REGEX="^(get|list|search)"
# Example: exclude delete operations
export DEFAULT_PROFILE_OPERATIONS_REGEX="^(?!delete)"Pros: Flexible, covers operation classes, adapts to API changes Cons: Risk of unintended matches, harder to audit
Option B: Blacklist (Exclusion-based)
export DEFAULT_PROFILE_EXCLUDE_OPERATIONS="deleteProject,deleteIssue"
export DEFAULT_PROFILE_EXCLUDE_TAGS="admin,deprecated"Pros: Include most, exclude specific dangerous operations
Current: redactQueryParam() supports exact key matching and now covers dot-separated parameter names, but standard URL parsing and fallback/manual parsing do not explicitly share one canonicalization strategy for encoded query keys or equivalent key spellings.
Goal: Keep query-parameter redaction deterministic across parser paths and reduce the risk of log leakage when sensitive keys appear in percent-encoded or otherwise equivalent forms.
Implementation:
- Define one canonicalization strategy for query parameter keys before comparison.
- Align
new URL()handling and manual fallback handling so both paths redact the same keys. - Add explicit tests for percent-encoded query keys and parser-path consistency.
- Keep the implementation bounded and regex-safe; do not introduce heuristic fuzzy matching.
Files to modify:
src/validation/validation-utils.tssrc/validation/validation-utils.test.ts
Estimated effort: 1-2 hours Cons: May miss new dangerous operations
Option C: Tag-based Filter (Leverages OpenAPI Tags)
export DEFAULT_PROFILE_INCLUDE_TAGS="projects,issues,merge_requests"
export DEFAULT_PROFILE_EXCLUDE_TAGS="admin,system"Pros: Semantic filtering aligned with API design Cons: Requires well-tagged OpenAPI spec
Recommendation:
- Production/Security-critical: Use Option A (whitelist) for explicit control
- Development/Exploration: Use Option A2 (regex) for flexibility
- Well-documented APIs: Add Option C (tag-based) for semantic filtering
- Combination: Support all three simultaneously (whitelist + regex + tags) with precedence: whitelist → regex → tags
Files to modify:
src/profile-loader.ts- filter operations increateDefaultProfile()README.md- document env variables
Estimated effort:
- Whitelist: 1 hour
- Regex: 1 hour
- Tag-based: 1-2 hours
- Total (all three): 3-4 hours
Problem: RegexCompiler accepts user input from HTTP headers (X-Mcp4-Tools) and environment variables. While RegexValidator provides partial protection (length limits, nested quantifiers, ambiguous alternation), it doesn't cover all ReDoS attack vectors. A malicious user could craft regex patterns that pass validation but still cause exponential backtracking.
Current protection:
- Max length: 100 characters (configurable)
- Nested quantifiers detection:
(a+)+,(x*)*, etc. - Ambiguous alternation detection:
(a|aa)+,(foo|foobar)*, etc.
Gaps:
- Other ReDoS patterns may pass validation
- No timeout for regex matching operations
- No limit on input length being tested against regex
Goal: Strengthen ReDoS protection to prevent DoS attacks via malicious regex patterns.
Implementation options:
Option A: Add Regex Matching Timeout (Recommended)
- Use worker threads or
piscinato run regex matches with timeout - Kill worker if match exceeds threshold (e.g., 100ms)
- Fallback to rejection if timeout occurs
Option B: Expand Validation Patterns
- Add detection for more ReDoS patterns (e.g., overlapping alternations, complex nested groups)
- Use regex analysis libraries (e.g.,
safe-regex,redos-detector)
Option C: Limit Test Input Length
- Restrict length of strings tested against compiled regex
- Tool names are naturally limited, but add explicit check in
CompiledRegex.test()
Recommendation: Implement Option A (timeout) + Option C (input length limit) for defense in depth. Option B can be added incrementally.
Files to modify:
src/tool-filter/regex/regex-compiler.ts- add timeout wrapper for regex matchingsrc/tool-filter/regex/regex-validator.ts- expand pattern detection (optional)src/tool-filter/regex/regex-compiler.test.ts- add timeout and edge case tests
Estimated effort: 2-3 hours (timeout implementation) + 1-2 hours (expanded validation)
Problem: HTTP profile routing keeps every requested profile's MCPServer initialized indefinitely. With many large OpenAPI specs, this can exhaust memory.
Goal: Add basic eviction or caps for profile server instances to bound memory usage.
Implementation options:
- TTL eviction: expire inactive profiles after
MCP4_PROFILE_CACHE_TTL_MS. - Max size: cap servers to
MCP4_PROFILE_CACHE_MAXwith LRU eviction. - Combine TTL + max size with soft warnings when evicting.
Files to modify:
src/mcp-server-manager.ts- eviction logic and trackingsrc/index.ts- pass env config into manager/registryREADME.md- document new env vars
Estimated effort: 2-4 hours
Problem: MCPServer holds a reference to HttpTransport (typed as any), creating a circular dependency and weaker type safety.
Goal: Remove direct dependency between MCPServer and HttpTransport for session cleanup and related hooks.
Implementation options:
- Introduce a small transport interface (methods used by MCPServer only) and type against that.
- Use event-based cleanup: MCPServer emits session cleanup events, HttpTransport subscribes.
- Invert control: move cleanup trigger into HttpTransport and pass a callback instead of full transport instance.
Files to modify:
src/mcp-server.ts- replace direct transport reference with interface/callbacksrc/http-transport.ts- adjust session cleanup hookupsrc/mcp-server-manager.ts- wiring changes if neededREADME.mdorIMPLEMENTATION.md- document architecture change
Estimated effort: 2-3 hours
Problem: src/http-transport.ts is >2700 lines. setupRoutes and related OAuth/MCP handlers are large and hard to maintain.
Goal: Improve maintainability by splitting HttpTransport into focused modules with clear responsibilities.
Implementation options:
- Extract OAuth handlers to
src/http/oauth-handler.ts(authorize, token, callback, metadata). - Extract MCP route setup to
src/http/mcp-router.ts(POST/GET/DELETE + SSE). - Keep core transport state in
src/http-transport.tsand delegate route registration.
Files to modify:
src/http-transport.ts- delegate to new modules- new module files under
src/http/ IMPLEMENTATION.md- architecture update (if needed)
Estimated effort: 3-5 hours
Problem: as any casts have grown, especially in src/http-transport.ts and related unit tests, weakening type safety.
Goal: Replace any casts with explicit interfaces or type guards where practical.
Implementation options:
- Introduce small internal interfaces for private helper shapes used in tests.
- Add type guards for request/response mocks in unit tests.
- Replace
anyinHttpTransportsignatures with typed callbacks or narrow types.
Files to modify:
src/http-transport.tssrc/http-transport.unit.test.tssrc/http-transport.test.ts
Estimated effort: 2-4 hours
Problem: Profile hint keys are derived from IP and user-agent only, so different clients behind the same NAT with identical user agents can overwrite each other's hints. This can misroute profile resolution for OAuth endpoints or origin checks when profile routing has no default profile.
Goal: Make profile hint association more robust to shared IPs and identical user-agent strings.
Implementation options:
- Include additional request entropy in the key (e.g., TLS session ID,
X-Forwarded-Forchain hash, or a server-generated hint cookie). - Set a hint cookie on first profile-routed request and use that as the primary key.
- Add a short-lived per-profile routing token embedded in OAuth metadata links.
Files to modify:
src/http-transport.ts- profile hint keying and lookup logicdocs/HTTP-TRANSPORT.md- document new hint mechanism (if user-facing)
Estimated effort: 2-4 hours
Problem: Profile hints are stored per client but only cleaned up on subsequent lookups, so many one-off clients can cause the cache to grow without bound in long-running servers.
Goal: Bound memory usage for profile hint cache.
Implementation options:
- Periodic cleanup timer that removes expired hints.
- Cap the cache size with LRU eviction.
- Combine TTL + size cap with logging when evictions occur.
Files to modify:
src/http-transport.ts- add cache eviction or cleanupREADME.md- document any new env vars or limits
Estimated effort: 1-2 hours
Current: ExternalOAuthProvider uses a denylist for dangerous redirect URI schemes (javascript:, data:, vbscript:, file:, blob:, about:) while keeping compatibility with custom client schemes.
Goal: Evaluate migrating to a strict allowlist-based scheme policy for stronger security guarantees.
Implementation options:
- Add
allowed_redirect_schemesto OAuth profile config with secure defaults (http,https) and explicit custom scheme opt-in. - Keep denylist as fallback behavior during migration to avoid breaking existing clients.
- Add compatibility tests for common deep-link clients and document migration guidance.
Files to modify:
src/types/profile.ts- OAuth config type extensionsrc/auth/oauth-provider.ts- scheme validation logicsrc/auth/oauth-provider.test.ts- allowlist and migration testsdocs/OAUTH.mdandREADME.md- configuration guidance
Estimated effort: 2-4 hours
Current: authFingerprint in src/transport/http-tenant-config.ts compares tenant auth compatibility via JSON.stringify over mapped auth config objects.
Analysis:
- Relevance is medium: current mapping to known keys reduces top-level key-order risks, so this is not an immediate correctness bug.
- Residual fragility remains for nested structures (for example
oauth_config) and long-term readability/maintainability. - A semantic comparator communicates intended equality rules better than string fingerprinting.
Goal: Replace serialization-based comparison with explicit deterministic auth config comparison.
Implementation options:
- Introduce dedicated utility (for example
areAuthConfigsEquivalent(left, right)). - Define explicit semantics for array order (sensitive vs insensitive) and document the choice.
- Perform field-level comparison for known auth interceptor properties, including nested
oauth_config. - Reuse comparator in tenant collision checks where
authFingerprint(...)is currently used. - Add focused tests for equivalent configs, non-equivalent configs, and nested OAuth config differences.
Files to modify:
src/transport/http-tenant-config.tssrc/transport/http-tenant-config.test.ts
Estimated effort: 1-2 hours
Problem: HttpTransport currently combines routing, auth, filtering, SSE/session control, and tenant-specific session state/lifecycle (tenantOAuthProvidersBySessionId, tenant selector consistency checks, tenant context hydration). This concentration increases coupling and regression risk.
Goal: Move tenant session responsibilities into a dedicated service while keeping transport behavior unchanged.
Implementation options:
- Introduce
TenantSessionService(orSessionManagerwith tenant-focused submodule) to own:- tenant context selection/validation for initialize and non-initialize requests
- session-level tenant OAuth provider map lifecycle (create/get/cleanup)
- tenant header immutability checks and error mapping
- Keep
HttpTransportas orchestrator only (HTTP parsing/routing + delegation). - Add small interfaces for session store access to avoid introducing new circular dependencies.
Files to modify:
src/transport/http-transport.tssrc/transport/http-tenant-config.ts(reuse and tighten public API boundaries)src/transport/http-transport.test.ts- optional new module(s):
src/transport/tenant-session-service.ts
Estimated effort: 3-5 hours
Background: Phase 3 (SasankaApiKeyStore) defers full SSRF validation (private IP range checks via SSRFValidator) to the first validate() call at session init time. Only the https: protocol check is enforced at profile load. A misconfigured base_url pointing at a private address (e.g., https://10.0.0.5/) passes profile loading and only fails on the first real client session.
Goal: Fail at profile load rather than at first session. Move the SSRFValidator.validate() call into ClientAuthGate construction (or a dedicated async init() method called from getProfileState()).
Benefits:
- Fail-fast: misconfigured
base_urlis caught during server startup or profile reload, not under real user load. - Operator clarity: the error surfaces in startup logs with a clear message, not buried in a session-init 401.
- Consistent with
JwksCache:JwksCachevalidates SSRF on every JWKS refresh; eager validation for Sasanka aligns the two auth paths. - No runtime overhead: SSRF check runs once at construction, not per session.
Implementation approach:
- Add
async init(): Promise<void>toClientAuthGatethat callsssrfValidator.validate(base_url)on theSasankaApiKeyStoreif configured. - In
getProfileState()(http-transport.ts), callawait state.clientAuthGate.init()after construction.
Trade-off: adds an async step to profile state initialization (first request for a profile, or server startup). Low overhead since it is a single DNS+TCP probe. The simpler current approach (lazy-with-flag) is acceptable for most deployments.
Files to modify:
src/auth/client-auth-gate.ts- addasync init()methodsrc/auth/sasanka-api-key-store.ts- exposevalidateSsrf(): Promise<void>src/transport/http-transport.ts::getProfileState()- awaitclientAuthGate.init()
Estimated effort: 1-2 hours