aegis_proxy_request
Make authenticated API calls through Aegis with automatic credential injection. Provide service name and API path; Aegis handles authentication securely without exposing credentials.
Instructions
Make an authenticated API call through Aegis. Credentials are injected automatically — you never see them. Provide the service name and API path; Aegis handles authentication.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| service | Yes | The service name (must match a registered credential in Aegis) | |
| path | Yes | The API path to call (e.g. "/v1/chat/completions") | |
| method | No | HTTP method (default: GET) | GET |
| headers | No | Additional request headers (auth headers are injected automatically) | |
| body | No | Request body (for POST/PUT/PATCH) | |
| targetHost | No | Override the target domain (must be in the credential's allowlist). Defaults to the credential's primary domain. |
Implementation Reference
- src/mcp/mcp-server.ts:156-221 (handler)The `registerProxyRequestTool` method defines the `aegis_proxy_request` MCP tool, handling input schema validation and calling the `proxyRequest` handler logic.
private registerProxyRequestTool(): void { this.server.registerTool( 'aegis_proxy_request', { title: 'Aegis Proxy Request', description: 'Make an authenticated API call through Aegis. Credentials are injected automatically — you never see them. Provide the service name and API path; Aegis handles authentication.', inputSchema: { service: z .string() .describe('The service name (must match a registered credential in Aegis)'), path: z.string().describe('The API path to call (e.g. "/v1/chat/completions")'), method: z .enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']) .optional() .default('GET') .describe('HTTP method (default: GET)'), headers: z .record(z.string(), z.string()) .optional() .describe('Additional request headers (auth headers are injected automatically)'), body: z.string().optional().describe('Request body (for POST/PUT/PATCH)'), targetHost: z .string() .optional() .describe( "Override the target domain (must be in the credential's allowlist). Defaults to the credential's primary domain.", ), }, }, async (args: { service: string; path: string; method?: string; headers?: Record<string, string>; body?: string; targetHost?: string; }) => { try { const result = await this.proxyRequest(args); return { content: [ { type: 'text' as const, text: JSON.stringify( { status: result.status, headers: result.headers, body: result.body, }, null, 2, ), }, ], }; } catch (err) { const message = err instanceof Error ? err.message : String(err); return { content: [{ type: 'text' as const, text: `Error: ${message}` }], isError: true, }; } }, ); } - src/mcp/mcp-server.ts:333-728 (handler)The `proxyRequest` method acts as the primary handler, implementing security controls (credential lookup, TTL, scope, policy, rate limiting, domain guard, body inspection) before performing the outbound request.
private async proxyRequest(params: { service: string; path: string; method?: string; headers?: Record<string, string>; body?: string; targetHost?: string; }): Promise<ProxyResult> { const method = params.method ?? 'GET'; const path = params.path.startsWith('/') ? params.path : `/${params.path}`; const requestId = generateRequestId(); // 1. Credential lookup const credential = this.vault.getByService(params.service); if (!credential) { this.metrics?.recordBlocked(params.service, 'no_credential', this.authenticatedAgent?.name); this.webhooks?.emit('blocked_request', { service: params.service, reason: 'no_credential', method, path, agent: this.authenticatedAgent?.name, }); this.ledger.logBlocked({ service: params.service, targetDomain: 'unknown', method, path, reason: `No credential found for service: ${params.service}`, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); throw new Error( `No credential registered for service: ${params.service}. ` + `Register one with: aegis vault add --name ${params.service} --service ${params.service} --secret YOUR_KEY --domains api.example.com`, ); } // 2. TTL enforcement if (this.vault.isExpired(credential)) { this.metrics?.recordBlocked( params.service, 'credential_expired', this.authenticatedAgent?.name, ); this.webhooks?.emit('credential_expiry', { service: params.service, credential: credential.name, expiredAt: credential.expiresAt, agent: this.authenticatedAgent?.name, }); this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason: `Credential "${credential.name}" expired at ${credential.expiresAt}`, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); throw new Error( `Credential "${credential.name}" has expired at ${credential.expiresAt}. ` + `Rotate with: aegis vault rotate --name ${credential.name} --secret NEW_SECRET`, ); } // 3. Agent credential scoping if (this.authenticatedAgent && this.agentRegistry) { if (!this.agentRegistry.hasAccess(this.authenticatedAgent.id, credential.id)) { this.metrics?.recordBlocked(params.service, 'agent_scope', this.authenticatedAgent.name); this.webhooks?.emit('blocked_request', { service: params.service, reason: 'agent_scope', agent: this.authenticatedAgent.name, credential: credential.name, method, path, }); this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason: `Agent "${this.authenticatedAgent.name}" not granted access to credential "${credential.name}"`, agentName: this.authenticatedAgent.name, agentTokenPrefix: this.authenticatedAgent.tokenPrefix, channel: 'mcp', }); throw new Error( `Agent "${this.authenticatedAgent.name}" is not granted access to credential "${credential.name}". ` + `Grant access with: aegis agent grant --agent ${this.authenticatedAgent.name} --credential ${credential.name}`, ); } } // 4. Credential scope enforcement if (!methodMatchesScope(method, credential.scopes)) { const scopeList = credential.scopes.join(', '); this.metrics?.recordBlocked( params.service, 'credential_scope', this.authenticatedAgent?.name, ); this.webhooks?.emit('blocked_request', { service: params.service, reason: 'credential_scope', credential: credential.name, method, scopes: credential.scopes, agent: this.authenticatedAgent?.name, path, }); this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason: `Method "${method}" not permitted by credential scopes [${scopeList}]`, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); throw new Error( `Method "${method}" is not permitted by credential scopes [${scopeList}]. ` + `Update scopes with: aegis vault update --name ${credential.name} --scopes ${scopeList},${method === 'GET' ? 'read' : 'write'}`, ); } // 5. Policy evaluation if (this.authenticatedAgent && this.policyMap.size > 0) { const agentPolicy = this.policyMap.get(this.authenticatedAgent.name); if (agentPolicy) { const evaluation = evaluatePolicy(agentPolicy, { service: params.service, method, path, }); if (!evaluation.allowed) { const reason = `Policy violation: ${evaluation.reason}`; if (this.policyMode === 'enforce') { this.metrics?.recordBlocked( params.service, 'policy_violation', this.authenticatedAgent.name, ); this.webhooks?.emit('blocked_request', { service: params.service, reason: 'policy_violation', agent: this.authenticatedAgent.name, violation: evaluation.violation, detail: evaluation.reason, method, path, }); this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason, agentName: this.authenticatedAgent.name, agentTokenPrefix: this.authenticatedAgent.tokenPrefix, channel: 'mcp', }); throw new Error( `Policy violation for agent "${this.authenticatedAgent.name}": ${evaluation.reason}`, ); } // Dry-run: log but allow this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason: `POLICY_DRY_RUN: ${evaluation.reason}`, agentName: this.authenticatedAgent.name, agentTokenPrefix: this.authenticatedAgent.tokenPrefix, channel: 'mcp', }); this.logger.info( { agent: this.authenticatedAgent.name, reason: evaluation.reason, requestId }, 'Dry-run: would block', ); } } } // 6. Agent rate limiting if (this.authenticatedAgent?.rateLimit) { try { const agentParsedLimit = parseRateLimit(this.authenticatedAgent.rateLimit); const agentResult = this.rateLimiter.check( `agent:${this.authenticatedAgent.id}`, agentParsedLimit, ); if (!agentResult.allowed) { this.metrics?.recordBlocked( params.service, 'agent_rate_limit', this.authenticatedAgent.name, ); this.webhooks?.emit('rate_limit_exceeded', { service: params.service, type: 'agent', agent: this.authenticatedAgent.name, limit: this.authenticatedAgent.rateLimit, retryAfter: agentResult.retryAfterSeconds, }); this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason: `Agent rate limit exceeded: ${this.authenticatedAgent.rateLimit} (retry after ${agentResult.retryAfterSeconds}s)`, agentName: this.authenticatedAgent.name, agentTokenPrefix: this.authenticatedAgent.tokenPrefix, channel: 'mcp', }); throw new Error( `Agent rate limit exceeded (${this.authenticatedAgent.rateLimit}). Retry after ${agentResult.retryAfterSeconds} seconds.`, ); } } catch (err) { // Re-throw rate limit errors, ignore parse errors if (err instanceof Error && err.message.includes('rate limit')) { throw err; } this.logger.error( { agent: this.authenticatedAgent.name, rateLimit: this.authenticatedAgent.rateLimit, requestId, }, 'Invalid agent rate limit config', ); } } // 7. Credential rate limiting if (credential.rateLimit) { try { const parsedLimit = parseRateLimit(credential.rateLimit); const result = this.rateLimiter.check(credential.id, parsedLimit); if (!result.allowed) { this.metrics?.recordBlocked( params.service, 'credential_rate_limit', this.authenticatedAgent?.name, ); this.webhooks?.emit('rate_limit_exceeded', { service: params.service, type: 'credential', credential: credential.name, limit: credential.rateLimit, retryAfter: result.retryAfterSeconds, agent: this.authenticatedAgent?.name, }); this.ledger.logBlocked({ service: params.service, targetDomain: credential.domains[0] ?? 'unknown', method, path, reason: `Rate limit exceeded: ${credential.rateLimit} (retry after ${result.retryAfterSeconds}s)`, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); throw new Error( `Rate limit exceeded for "${credential.name}" (${credential.rateLimit}). Retry after ${result.retryAfterSeconds} seconds.`, ); } } catch (err) { if (err instanceof Error && err.message.includes('Rate limit')) { throw err; } this.logger.error( { credential: credential.name, rateLimit: credential.rateLimit, requestId }, 'Invalid credential rate limit config', ); } } // 8. Domain guard const targetDomain = params.targetHost ?? credential.domains[0]; if (!this.vault.domainMatches(targetDomain, credential.domains)) { this.metrics?.recordBlocked(params.service, 'domain_guard', this.authenticatedAgent?.name); this.webhooks?.emit('blocked_request', { service: params.service, reason: 'domain_guard', targetDomain, allowedDomains: credential.domains, agent: this.authenticatedAgent?.name, method, path, }); this.ledger.logBlocked({ service: params.service, targetDomain, method, path, reason: `Domain "${targetDomain}" not in allowlist [${credential.domains.join(', ')}]`, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); throw new Error( `Domain "${targetDomain}" is not in the credential's allowlist [${credential.domains.join(', ')}].`, ); } // 9. Body inspection if (credential.bodyInspection !== 'off' && params.body && params.body.length > 0) { const inspection = this.bodyInspector.inspect(params.body); if (inspection.suspicious) { const matchSummary = inspection.matches.join('; '); if (credential.bodyInspection === 'block') { this.metrics?.recordBlocked( params.service, 'body_inspection', this.authenticatedAgent?.name, ); this.webhooks?.emit('body_inspection', { service: params.service, credential: credential.name, matches: inspection.matches, agent: this.authenticatedAgent?.name, method, path, }); this.ledger.logBlocked({ service: params.service, targetDomain, method, path, reason: `Body inspection: potential credential exfiltration — ${matchSummary}`, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); throw new Error( `Request body contains credential-like patterns: ${matchSummary}. ` + "If this is intentional, set body inspection to 'warn' or 'off' for this credential.", ); } // Warn mode this.logger.warn( { credential: credential.name, matches: inspection.matches, requestId }, 'Body inspection: credential-like patterns detected (warn mode)', ); } } // 10. Make the outbound request with credential injection const result = await this.makeOutboundRequest(credential, { targetDomain, path, method, headers: params.headers, body: params.body, }); // 11. Audit log this.ledger.logAllowed({ credentialId: credential.id, credentialName: credential.name, service: params.service, targetDomain, method, path, responseCode: result.status, agentName: this.authenticatedAgent?.name, agentTokenPrefix: this.authenticatedAgent?.tokenPrefix, channel: 'mcp', }); this.logger.info( { service: params.service, method, path, status: result.status, requestId }, 'MCP request proxied', ); this.metrics?.recordRequest( params.service, method, result.status, this.authenticatedAgent?.name, ); return result; }