|
26 | 26 | apply_connection_params, |
27 | 27 | connection_display_fields, |
28 | 28 | connection_field_labels, |
| 29 | + format_page_footer, |
| 30 | + format_page_info, |
29 | 31 | infer_mode, |
30 | 32 | parse_sql_flavor, |
31 | 33 | resolve_connection, |
32 | 34 | validate_connection_fields, |
| 35 | + validate_limit, |
| 36 | + validate_page, |
33 | 37 | ) |
34 | 38 | from testgen.mcp.tools.markdown import MdDoc |
35 | 39 |
|
36 | 40 |
|
| 41 | +@with_database_session |
| 42 | +@mcp_permission("view") |
| 43 | +def list_connections(project_code: str, page: int = 1, limit: int = 20) -> str: |
| 44 | + """List database connections in a project with their database type and table-group counts. |
| 45 | +
|
| 46 | + Use this before changing or referencing a connection to confirm its ID, name, and host. |
| 47 | + Credentials are never returned. |
| 48 | +
|
| 49 | + Args: |
| 50 | + project_code: The project code to list connections for. |
| 51 | + page: Page number starting at 1 (default 1). |
| 52 | + limit: Page size (default 20, max 100). |
| 53 | + """ |
| 54 | + validate_page(page) |
| 55 | + validate_limit(limit, 100) |
| 56 | + |
| 57 | + perms = get_project_permissions() |
| 58 | + perms.verify_access(project_code, not_found=MCPResourceNotAccessible("Project", project_code)) |
| 59 | + |
| 60 | + rows, total = Connection.list_for_project(project_code, page=page, limit=limit) |
| 61 | + |
| 62 | + if not rows: |
| 63 | + if page > 1: |
| 64 | + return f"No connections on page {page} (total: {total})." |
| 65 | + return f"No connections found for project `{project_code}`." |
| 66 | + |
| 67 | + doc = MdDoc() |
| 68 | + doc.heading(1, f"Connections for `{project_code}`") |
| 69 | + doc.text(format_page_info(total, page, limit)) |
| 70 | + table_rows: list[list[object]] = [] |
| 71 | + for row in rows: |
| 72 | + label = SQL_FLAVOR_CODE_TO_LABEL.get(row.sql_flavor_code) |
| 73 | + database_type = label.value if label else row.sql_flavor_code |
| 74 | + table_rows.append( |
| 75 | + [ |
| 76 | + row.connection_id, |
| 77 | + row.connection_name, |
| 78 | + database_type, |
| 79 | + row.project_host, |
| 80 | + row.project_db, |
| 81 | + row.table_group_count, |
| 82 | + ] |
| 83 | + ) |
| 84 | + doc.table( |
| 85 | + ["ID", "Name", "Type", "Host", "Database", "Table groups"], |
| 86 | + table_rows, |
| 87 | + code=[0, 3, 4], |
| 88 | + ) |
| 89 | + if footer := format_page_footer(total, page, limit): |
| 90 | + doc.text(footer) |
| 91 | + return doc.render() |
| 92 | + |
| 93 | + |
| 94 | +@with_database_session |
| 95 | +@mcp_permission("view") |
| 96 | +def get_connection(connection_id: int) -> str: |
| 97 | + """Get a connection's configuration, including database type, host, and authentication mode. |
| 98 | +
|
| 99 | + Credentials (password, private key, service-account key) are never returned. |
| 100 | + Use this before editing a connection or creating a table group on it. |
| 101 | +
|
| 102 | + Args: |
| 103 | + connection_id: Bigint connection ID returned by `list_connections` or shown on the connections page. |
| 104 | + """ |
| 105 | + connection = resolve_connection(connection_id) |
| 106 | + |
| 107 | + doc = MdDoc() |
| 108 | + doc.heading(1, f"Connection `{connection.connection_name}`") |
| 109 | + doc.field("ID", connection.connection_id, code=True) |
| 110 | + doc.field("Project", connection.project_code, code=True) |
| 111 | + label = SQL_FLAVOR_CODE_TO_LABEL.get(connection.sql_flavor_code) |
| 112 | + doc.field("Type", label.value if label else connection.sql_flavor_code) |
| 113 | + |
| 114 | + if connection.project_host: |
| 115 | + doc.field("Host", connection.project_host, code=True) |
| 116 | + if connection.project_port: |
| 117 | + doc.field("Port", connection.project_port) |
| 118 | + if connection.project_db: |
| 119 | + doc.field("Database", connection.project_db, code=True) |
| 120 | + if connection.project_user: |
| 121 | + doc.field("User", connection.project_user, code=True) |
| 122 | + if connection.connect_by_url and connection.url: |
| 123 | + doc.field("URL", connection.url, code=True) |
| 124 | + if connection.warehouse: |
| 125 | + doc.field("Warehouse", connection.warehouse, code=True) |
| 126 | + if connection.http_path: |
| 127 | + doc.field("HTTP Path", connection.http_path, code=True) |
| 128 | + doc.field("Authentication", _authentication_label(connection)) |
| 129 | + |
| 130 | + if connection.max_threads is not None: |
| 131 | + doc.field("Max Threads", connection.max_threads) |
| 132 | + if connection.max_query_chars is not None: |
| 133 | + doc.field("Max Expression Length", connection.max_query_chars) |
| 134 | + |
| 135 | + return doc.render() |
| 136 | + |
| 137 | + |
37 | 138 | @with_database_session |
38 | 139 | @mcp_permission("administer") |
39 | 140 | def test_connection( |
|
0 commit comments