diff --git a/infisical_sdk/api_types.py b/infisical_sdk/api_types.py index 80eb8f1..85b9eb0 100644 --- a/infisical_sdk/api_types.py +++ b/infisical_sdk/api_types.py @@ -567,11 +567,11 @@ class AgentVaultSessionAccessBundle(BaseModel): @dataclass class AgentVaultInlineService(BaseModel): - """A service sent with an Agent Vault session, without its token, password or values. Its nested settings, such as credential, are left as the API returns them.""" + """A service sent with an Agent Vault session, without its token, password or values. type is http, mcp, postgres, mysql or ssh, and the API returns only that type's fields: hostPattern, allowedMethods, allowedPathPrefixes, customHeaders and substitutions for http; serverUrl, transport, hostPattern and customHeaders for mcp; host, port, database and tls for postgres and mysql; host and port for ssh. Its nested settings, such as credential and tls, are left as the API returns them.""" id: str name: str type: str - hostPattern: str + hostPattern: Optional[str] = None serverUrl: Optional[str] = None transport: Optional[str] = None allowedMethods: Optional[List[str]] = None @@ -579,6 +579,11 @@ class AgentVaultInlineService(BaseModel): credential: Optional[Dict[str, Any]] = None customHeaders: Optional[List[Dict[str, Any]]] = None substitutions: Optional[List[Dict[str, Any]]] = None + description: Optional[str] = None + host: Optional[str] = None + port: Optional[int] = None + database: Optional[str] = None + tls: Optional[Dict[str, Any]] = None @dataclass class AgentVaultSession(BaseModel): @@ -682,12 +687,12 @@ class AgentVaultAccessBundleRef(BaseModel): @dataclass class AgentVaultService(BaseModel): - """A service in an Agent Vault access bundle. Its nested settings, such as credential, are left as the API returns them.""" + """A service in an Agent Vault access bundle. type is http, mcp, postgres, mysql or ssh, and the API returns only that type's fields: hostPattern, allowedMethods, allowedPathPrefixes, customHeaders and substitutions for http; serverUrl, transport, hostPattern, allowedTools and customHeaders for mcp; host, port, database and tls for postgres and mysql; host and port for ssh. Its nested settings, such as credential and tls ({"enabled", "verify", "ca"}), are left as the API returns them.""" id: str accessBundleId: str name: str type: str - hostPattern: str + hostPattern: Optional[str] = None serverUrl: Optional[str] = None transport: Optional[str] = None allowedMethods: Optional[List[str]] = None @@ -700,6 +705,11 @@ class AgentVaultService(BaseModel): createdAt: Optional[str] = None updatedAt: Optional[str] = None allowedTools: Optional[List[str]] = None + description: Optional[str] = None + host: Optional[str] = None + port: Optional[int] = None + database: Optional[str] = None + tls: Optional[Dict[str, Any]] = None @dataclass class AgentVaultAccessBundle(BaseModel): diff --git a/infisical_sdk/resources/agent_vault.py b/infisical_sdk/resources/agent_vault.py index 8069ad8..d7da6b9 100644 --- a/infisical_sdk/resources/agent_vault.py +++ b/infisical_sdk/resources/agent_vault.py @@ -346,7 +346,7 @@ def create( label: A short description of what the session is for, such as "nightly deploy". Up to 128 characters, without line breaks, tabs, or invisible characters. on_behalf_of_user_email: The email address of the user to start the session on behalf of. Can't be combined with on_behalf_of_user_id. on_behalf_of_user_id: The ID of the user to start the session on behalf of. Can't be combined with on_behalf_of_user_email. - services: Services the session carries itself, instead of or beside its access bundle's: up to 20 dicts with the API's field names, such as {"type": "http", "hostPattern": "api.github.com", "credential": {"type": "bearer", "value": token}}. Only Agent Vault admins can send them. Each has the same fields as a service in an access bundle, except that its name is optional, its values can't use variables, it can't use OAuth or member-provided values, and an MCP server can't be limited to some of its tools. To limit an MCP server's tools, add it to an access bundle instead. + services: Services the session carries itself, instead of or beside its access bundle's: up to 20 dicts with the API's field names, such as {"type": "http", "hostPattern": "api.github.com", "credential": {"type": "bearer", "value": token}}. Only Agent Vault admins can send them. Each has the same fields as a service in an access bundle, except that its name is optional for HTTP and MCP services (database and SSH services need one), its values can't use variables, it can't use OAuth or member-provided values, and an MCP server can't be limited to some of its tools. To limit an MCP server's tools, add it to an access bundle instead. project_id: The ID of the Agent Vault project to create the session in. Returns: diff --git a/tests/test_agent_vault.py b/tests/test_agent_vault.py index 99c3f9d..d7711fb 100644 --- a/tests/test_agent_vault.py +++ b/tests/test_agent_vault.py @@ -442,14 +442,44 @@ def test_access_bundles_get_reads_the_tools_an_mcp_service_allows(api, agent_vau service = { "id": SERVICE_ID, "accessBundleId": BUNDLE["id"], "name": "linear", "type": "mcp", "serverUrl": "https://mcp.linear.app/mcp", "transport": "streamable-http", "hostPattern": "mcp.linear.app", - "allowedMethods": None, "allowedPathPrefixes": None, "allowedTools": ["list_issues"], - "credential": {"type": "bearer", "memberProvidedFields": []}, "customHeaders": [], "substitutions": [], + "allowedTools": ["list_issues"], + "credential": {"type": "bearer", "memberProvidedFields": []}, "customHeaders": [], "createdAt": BUNDLE["createdAt"], "updatedAt": BUNDLE["updatedAt"], } api.session.get.return_value = json_response({"accessBundle": dict(BUNDLE, services=[service])}) assert agent_vault.access_bundles.get(BUNDLE["id"]).services[0].allowedTools == ["list_issues"] +def test_access_bundles_get_reads_database_and_ssh_services_without_http_fields(api, agent_vault, json_response): + shared = { + "accessBundleId": BUNDLE["id"], "description": None, "variableReferences": [], "createdAt": BUNDLE["createdAt"], "updatedAt": BUNDLE["updatedAt"], + } + postgres = dict(shared, id=SERVICE_ID, name="orders-db", type="postgres", host="db.internal", port=5432, + database="orders", tls={"enabled": True, "verify": True, "ca": None}, + credential={"type": "basic", "memberProvidedFields": []}) + ssh = dict(shared, id="7d1e3f5a-9b2c-4d6e-8f0a-1b3c5d7e9f2a", name="bastion", type="ssh", + host="bastion.internal", port=22, credential={"type": "ssh-key", "hasCertificate": False}) + api.session.get.return_value = json_response({"accessBundle": dict(BUNDLE, services=[postgres, ssh])}) + database, server = agent_vault.access_bundles.get(BUNDLE["id"]).services + assert (database.host, database.port, database.database) == ("db.internal", 5432, "orders") + assert database.tls == {"enabled": True, "verify": True, "ca": None} + assert database.hostPattern is None + assert (server.host, server.port, server.tls) == ("bastion.internal", 22, None) + + +def test_sessions_read_an_inline_database_service(api, agent_vault, json_response): + inline = { + "id": INLINE_SERVICE["id"], "name": "orders-db", "description": "Orders replica", "type": "mysql", "host": "db.internal", "port": 3306, + "database": "orders", "tls": {"enabled": False, "verify": False, "ca": None}, + "credential": {"type": "basic"}, + } + api.session.get.return_value = json_response({"session": dict(SESSION, services=[inline])}) + service = agent_vault.sessions.get(SESSION["id"]).services[0] + assert service == AgentVaultInlineService(**inline) + assert service.hostPattern is None and service.customHeaders is None + assert service.description == "Orders replica" + + def test_access_bundles_list_grantees_reads_a_pending_organization_membership(api, agent_vault, json_response): grantee = dict({key: ON_BEHALF_OF[key] for key in ("type", "id", "username", "email", "firstName", "lastName")}, isOrgMembershipPending=True, grantedAt="2026-10-27T00:00:00.000Z")