chore: merge dev into main � MCP underscore convention + new features #8

Merged
jmiller merged 7 commits from dev into main 2026-05-19 19:59:49 +00:00
6 changed files with 1 additions and 840 deletions
View File
-589
View File
@@ -1,589 +0,0 @@
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: joomla-api-mcp.Documentation
INGROUP: joomla-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/joomla-api-mcp
PATH: /docs/API.md
VERSION: 01.01.00
BRIEF: MCP tool reference documentation — 67 tools
-->
# API Reference
All tools accept an optional `connection` parameter to target a specific named connection. If omitted, the default connection is used.
## Articles
### `joomla_articles_list`
List articles with optional filtering.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `category_id` | number | No | Filter by category ID |
| `state` | `"0"` / `"1"` / `"-2"` | No | 1=published, 0=unpublished, -2=trashed |
| `search` | string | No | Search in title |
| `limit` | number | No | Max results (default 20) |
| `offset` | number | No | Pagination offset |
### `joomla_article_get`
Get a single article by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Article ID |
### `joomla_article_create`
Create a new article.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Article title |
| `articletext` | string | Yes | Article body (HTML) |
| `catid` | number | Yes | Category ID |
| `state` | number | No | 1=published, 0=unpublished (default 0) |
| `language` | string | No | Language code (default `"*"`) |
| `featured` | number | No | 1=featured, 0=not |
| `metadesc` | string | No | Meta description |
| `metakey` | string | No | Meta keywords |
### `joomla_article_update`
Update an existing article. Only provided fields are changed.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Article ID |
| `title` | string | No | New title |
| `articletext` | string | No | New body (HTML) |
| `catid` | number | No | New category ID |
| `state` | number | No | State value |
| `featured` | number | No | Featured flag |
| `metadesc` | string | No | Meta description |
### `joomla_article_delete`
Delete an article.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Article ID |
## Categories
### `joomla_categories_list`
List content categories.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `extension` | string | No | Extension name (default `"com_content"`) |
| `search` | string | No | Search in title |
### `joomla_category_create`
Create a new category.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Category title |
| `parent_id` | number | No | Parent category ID (default 1 = root) |
| `extension` | string | No | Extension (default `"com_content"`) |
| `description` | string | No | Category description |
| `state` | number | No | 1=published, 0=unpublished |
| `language` | string | No | Language code (default `"*"`) |
### `joomla_category_update`
Update a category.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Category ID |
| `title` | string | No | New title |
| `description` | string | No | New description |
| `state` | number | No | State value |
### `joomla_category_delete`
Delete a category.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Category ID |
## Users
### `joomla_users_list`
List Joomla users.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `search` | string | No | Search in name/username/email |
| `group_id` | number | No | Filter by user group ID |
| `state` | `"0"` / `"1"` | No | 0=blocked, 1=active |
| `limit` | number | No | Max results |
### `joomla_user_get`
Get a single user by ID.
### `joomla_user_create`
Create a user with auto-generated secure password.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Full name |
| `username` | string | Yes | Username |
| `email` | string | Yes | Email address |
| `groups` | number[] | No | Group IDs (default `[2]` = Registered) |
| `block` | number | No | 0=active, 1=blocked (default 0) |
Returns the generated password in the response. Share securely with the user.
### `joomla_user_update`
Update a user.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | User ID |
| `name` | string | No | Full name |
| `email` | string | No | Email |
| `groups` | number[] | No | Group IDs |
| `block` | number | No | 0=active, 1=blocked |
### `joomla_user_delete`
Delete a user.
### `joomla_user_groups_list`
List all user groups. No parameters.
## Contacts
### `joomla_contacts_list`
List contacts with optional search.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `search` | string | No | Search in name |
### `joomla_contact_get`
Get a single contact by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Contact ID |
### `joomla_contact_create`
Create a new contact.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Contact name |
| `alias` | string | No | URL alias |
| `catid` | number | No | Category ID |
| `email_to` | string | No | Email address |
| `telephone` | string | No | Phone number |
| `address` | string | No | Street address |
| `suburb` | string | No | City/suburb |
| `state` | string | No | State/province |
| `postcode` | string | No | Postal code |
| `country_id` | number | No | Country ID |
| `published` | number | No | 1=published, 0=unpublished |
| `language` | string | No | Language code (default `"*"`) |
### `joomla_contact_update`
Update an existing contact.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Contact ID |
| `name` | string | No | Contact name |
| `email_to` | string | No | Email address |
| `telephone` | string | No | Phone number |
| `address` | string | No | Street address |
| `published` | number | No | 1=published, 0=unpublished |
### `joomla_contact_delete`
Delete a contact.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Contact ID |
## Menus
### `joomla_menus_list`
List menu types. No parameters.
### `joomla_menu_items_list`
List menu items for a menu type.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `menutype` | string | Yes | Menu type alias (e.g. `"mainmenu"`) |
### `joomla_menu_item_get`
Get a single menu item by ID.
### `joomla_menu_item_create`
Create a new menu item.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Menu item title |
| `menutype` | string | Yes | Menu type alias (e.g. `"mainmenu"`) |
| `type` | string | Yes | Item type (`"component"`, `"url"`, `"alias"`, `"separator"`, `"heading"`) |
| `link` | string | No | URL or component link |
| `parent_id` | number | No | Parent menu item ID (default 1 = root) |
| `published` | number | No | 1=published, 0=unpublished |
| `access` | number | No | Access level ID |
| `language` | string | No | Language code (default `"*"`) |
### `joomla_menu_item_update`
Update a menu item.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Menu item ID |
| `title` | string | No | New title |
| `link` | string | No | New link URL |
| `published` | number | No | 1=published, 0=unpublished |
| `parent_id` | number | No | New parent ID |
### `joomla_menu_item_delete`
Delete a menu item.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Menu item ID |
## Plugins
### `joomla_plugins_list`
List plugins.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type` | string | No | Filter by plugin folder (e.g. `"system"`, `"content"`) |
| `state` | `"0"` / `"1"` | No | 0=disabled, 1=enabled |
| `search` | string | No | Search in name |
### `joomla_plugin_update`
Enable or disable a plugin.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Plugin ID |
| `enabled` | number | Yes | 1=enable, 0=disable |
## Modules
### `joomla_modules_list`
List site or admin modules.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `client_id` | `"0"` / `"1"` | No | 0=site, 1=admin |
## Templates
### `joomla_templates_list`
List site or admin templates.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `client_id` | `"0"` / `"1"` | No | 0=site, 1=admin |
## Tags
### `joomla_tags_list`
List tags with optional search.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `search` | string | No | Search in title |
### `joomla_tag_get`
Get a single tag by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Tag ID |
### `joomla_tag_create`
Create a tag.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Tag title |
| `parent_id` | number | No | Parent tag ID |
### `joomla_tag_update`
Update a tag.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Tag ID |
| `title` | string | No | New tag title |
| `published` | number | No | 1=published, 0=unpublished |
### `joomla_tag_delete`
Delete a tag.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Tag ID |
## Custom Fields
### `joomla_fields_list`
List custom fields for a context (default `"com_content.article"`).
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | No | Context (default `"com_content.article"`) |
### `joomla_field_get`
Get a single custom field by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Field ID |
### `joomla_field_create`
Create a custom field.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | Yes | Field title |
| `name` | string | Yes | Field name (system identifier) |
| `type` | string | Yes | Field type (text, textarea, list, radio, checkboxes, etc.) |
| `context` | string | No | Context (default `"com_content.article"`) |
| `label` | string | No | Display label |
| `description` | string | No | Field description |
| `required` | number | No | 1=required, 0=optional |
| `state` | number | No | 1=published, 0=unpublished |
### `joomla_field_delete`
Delete a custom field.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Field ID |
## Banners
### `joomla_banners_list`
List banners. No parameters.
### `joomla_banner_get`
Get a single banner by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Banner ID |
### `joomla_banner_create`
Create a new banner.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Banner name |
| `catid` | number | No | Category ID |
| `clickurl` | string | No | Click URL |
| `custombannercode` | string | No | Custom HTML/code |
| `state` | number | No | 1=published, 0=unpublished |
### `joomla_banner_delete`
Delete a banner.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Banner ID |
### `joomla_banner_clients_list`
List banner clients. No parameters.
## Newsfeeds
### `joomla_newsfeeds_list`
List newsfeeds. No parameters.
### `joomla_newsfeed_get`
Get a single newsfeed by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Newsfeed ID |
### `joomla_newsfeed_create`
Create a new newsfeed.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Feed name |
| `link` | string | Yes | Feed URL |
| `catid` | number | Yes | Category ID |
| `numarticles` | number | No | Number of articles to display |
| `published` | number | No | 1=published, 0=unpublished |
| `language` | string | No | Language code (default `"*"`) |
### `joomla_newsfeed_delete`
Delete a newsfeed.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Newsfeed ID |
## Messages
### `joomla_messages_list`
List private messages. No parameters.
### `joomla_message_get`
Get a single private message by ID.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Message ID |
### `joomla_message_send`
Send a private message to a Joomla user.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `user_id_to` | number | Yes | Recipient user ID |
| `subject` | string | Yes | Message subject |
| `message` | string | Yes | Message body |
### `joomla_message_delete`
Delete a private message.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Message ID |
## Media
### `joomla_media_list`
List media files in a folder.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | No | Folder path relative to media root |
### `joomla_media_file_get`
Get metadata for a specific media file.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | Yes | File path relative to media root (e.g. `"images/logo.png"`) |
### `joomla_media_file_delete`
Delete a media file.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | Yes | File path relative to media root |
### `joomla_media_folder_create`
Create a new media folder.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | string | Yes | Full folder path to create (e.g. `"images/photos/2026"`) |
## Redirects
### `joomla_redirects_list`
List URL redirects.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `search` | string | No | Search in old URL |
| `state` | `"0"` / `"1"` / `"2"` / `"-2"` | No | 0=disabled, 1=enabled, 2=archived, -2=trashed |
### `joomla_redirect_create`
Create a URL redirect.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `old_url` | string | Yes | Source URL to redirect from |
| `new_url` | string | Yes | Destination URL to redirect to |
| `status_code` | `"301"` / `"302"` | No | 301=permanent, 302=temporary (default 301) |
| `published` | number | No | 1=enabled, 0=disabled |
### `joomla_redirect_delete`
Delete a URL redirect.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | number | Yes | Redirect ID |
## Content History
### `joomla_content_history_list`
List version history for a content item.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `type_alias` | string | Yes | Content type alias (e.g. `"com_content.article"`) |
| `item_id` | number | Yes | Item ID |
## Checkin
### `joomla_checkin`
Check in (unlock) a content item that is checked out.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Context (e.g. `"com_content.article"`) |
| `id` | number | Yes | Item ID to check in |
## Associations
### `joomla_associations_list`
List multilingual associations for a content item.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `context` | string | Yes | Context (e.g. `"com_content.article"`) |
| `id` | number | Yes | Item ID to get associations for |
## Configuration
### `joomla_config_get`
Get application configuration. No parameters.
### `joomla_config_update`
Update application configuration values.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `settings` | object | Yes | Key-value pairs of settings to update |
## Languages
### `joomla_languages_list`
List installed content languages. No parameters.
## Generic
### `joomla_api_request`
Make a raw API request to any Joomla Web Services endpoint.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `method` | `"GET"` / `"POST"` / `"PATCH"` / `"DELETE"` | Yes | HTTP method |
| `endpoint` | string | Yes | API path (e.g. `"/content/articles"`) |
| `body` | object | No | Request body for POST/PATCH |
| `params` | object | No | Query parameters |
### `joomla_list_connections`
List all configured connections. No parameters.
## Revision History
| Date | Version | Author | Notes |
| --- | --- | --- | --- |
| 2026-04-23 | 0.0.1 | jmiller | Initial API reference (36 tools) |
| 2026-05-07 | 0.1.0 | jmiller | Expanded to 67 tools — full CRUD for contacts, banners, newsfeeds, tags, fields, menu items, messages, media, redirects, associations, checkin, content history |
-80
View File
@@ -1,80 +0,0 @@
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: joomla-api-mcp.Documentation
INGROUP: joomla-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/joomla-api-mcp
PATH: /docs/ARCHITECTURE.md
VERSION: 00.00.01
BRIEF: Architecture overview and design decisions
-->
# Architecture
## Overview
joomla-api-mcp is a Model Context Protocol (MCP) server that bridges AI assistants (Claude Code, Cursor, etc.) with Joomla's built-in Web Services REST API.
```
AI Assistant <--> MCP (stdio) <--> JoomlaClient <--> Joomla REST API
/api/index.php/v1
```
## Components
### `src/index.ts` — Server Entry Point
Registers all MCP tools with the `McpServer` from `@modelcontextprotocol/sdk`. Each tool maps to one or more Joomla API endpoints. Uses Zod schemas for input validation.
### `src/client.ts` — HTTP Client
The `JoomlaClient` class handles all HTTP communication with Joomla instances:
- Uses `node:https` / `node:http` for requests (not `fetch`) to support self-signed TLS certificates on Node.js 24+
- Authenticates via Bearer token in the `Authorization` header
- Sends `Accept: application/vnd.api+json` per Joomla's JSON:API spec
- Includes a JSON parser that handles Joomla responses that may append HTML error fragments after valid JSON
### `src/config.ts` — Configuration Loader
Loads connection details from `~/.joomla-api-mcp.json`. Supports multiple named connections with a configurable default.
### `src/types.ts` — Type Definitions
TypeScript interfaces for `JoomlaConnection`, `JoomlaConfig`, and `ApiResponse`.
### `scripts/setup.mjs` — Interactive Setup
Node.js script using `readline/promises` that walks users through creating the config file. Supports adding multiple connections incrementally.
## Design Decisions
### Why `node:https` instead of `fetch`?
Node.js 24's built-in `fetch` (undici-based) does not honor `NODE_TLS_REJECT_UNAUTHORIZED=0` for self-signed certificate bypass. The classic `node:https` module with `rejectUnauthorized: false` works reliably across all Node.js versions.
### Why JSON recovery parsing?
Joomla's API sometimes returns valid JSON with `text/html` content-type, and may append HTML error fragments (e.g. template errors) after the JSON body. The `tryParseJson` method handles this by finding the last valid JSON boundary when standard parsing fails.
### Why per-connection API tokens?
Each Joomla site requires its own API token scoped to a specific user. Multi-site support is a core use case — managing staging, production, and dev environments from a single MCP server.
## Data Flow
1. AI assistant sends a tool call via MCP stdio transport
2. `index.ts` validates parameters with Zod and resolves the connection
3. `JoomlaClient` constructs the API URL, attaches auth headers, and makes the HTTP request
4. Response is parsed (with JSON recovery if needed) and returned as MCP tool output
## Revision History
| Date | Version | Author | Notes |
| --- | --- | --- | --- |
| 2026-04-23 | 0.0.1 | jmiller | Initial architecture document |
-145
View File
@@ -1,145 +0,0 @@
<!--
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: joomla-api-mcp.Documentation
INGROUP: joomla-api-mcp
REPO: https://git.mokoconsulting.tech/MokoConsulting/joomla-api-mcp
PATH: /docs/INSTALLATION.md
VERSION: 00.00.01
BRIEF: Installation and setup instructions
-->
# Installation
## Prerequisites
- **Node.js** 20.0.0 or later
- **npm** (included with Node.js)
- A Joomla 4/5/6 site with the Web Services API enabled
- A Joomla API token (generated in Users > Edit User > API Token tab)
## Install
```sh
git clone https://git.mokoconsulting.tech/MokoConsulting/joomla-api-mcp.git
cd joomla-api-mcp
npm install
npm run build
npm run setup
```
The setup wizard will prompt for:
1. **Connection name** — a label for this site (e.g. `production`, `staging`)
2. **Joomla site URL** — the base URL of the site (e.g. `https://www.example.com`)
3. **API token** — the Joomla API token for authentication
4. **TLS verification** — whether to skip certificate verification (for self-signed certs)
Run `npm run setup` again to add more connections.
## Register with Claude Code
Add to your global Claude Code config (`~/.claude.json`):
```json
{
"mcpServers": {
"joomla-api": {
"type": "stdio",
"command": "node",
"args": ["/path/to/joomla-api-mcp/dist/index.js"]
}
}
}
```
Or add to a project-level `.mcp.json`:
```json
{
"mcpServers": {
"joomla-api": {
"command": "node",
"args": ["/path/to/joomla-api-mcp/dist/index.js"]
}
}
}
```
Restart Claude Code after adding the server.
## Generating a Joomla API Token
1. Log in to the Joomla admin panel
2. Go to **Users** > **Manage** > select your user
3. Click the **API Token** tab
4. Click **Save** to generate a token
5. Copy the token value — it will not be shown again
The token must belong to a user with sufficient permissions for the operations you want to perform (e.g. Super Users for full access).
## Configuration File
The config is stored at `~/.joomla-api-mcp.json`:
```json
{
"defaultConnection": "production",
"connections": {
"production": {
"baseUrl": "https://www.example.com",
"apiToken": "your-api-token"
},
"staging": {
"baseUrl": "https://staging.example.com",
"apiToken": "your-staging-token",
"insecure": true
}
}
}
```
| Field | Required | Description |
|-------|----------|-------------|
| `defaultConnection` | Yes | Name of the default connection |
| `connections` | Yes | Map of named connections |
| `baseUrl` | Yes | Joomla site URL (no trailing slash) |
| `apiToken` | Yes | Joomla API token (Bearer auth) |
| `insecure` | No | Set `true` to skip TLS verification |
## Verification
After setup, verify the server starts correctly:
```sh
npm start
```
If configured correctly, the server will start listening on stdio. If the config is missing or invalid, it will print an error with instructions.
## Troubleshooting
### "Failed to load config" error
The config file `~/.joomla-api-mcp.json` is missing or malformed. Run `npm run setup` to create it.
### "terminated" or connection errors
- Verify the Joomla site is reachable from your machine
- For self-signed certs, set `"insecure": true` in the connection config
- Ensure the API token is valid and not expired
### "Resource not found" on certain endpoints
Some Joomla API endpoints require specific components to be installed and enabled. For example, the menus API requires `com_menus` to expose its Web Services routes.
## Revision History
| Date | Version | Author | Notes |
| --- | --- | --- | --- |
| 2026-04-23 | 0.0.1 | jmiller | Initial installation guide |
-25
View File
@@ -1,25 +0,0 @@
# Docs Index: /templates/docs
## Purpose
This index provides navigation to documentation within this folder.
## Subfolders
- [extra/](./extra/index.md)
- [required/](./required/index.md)
## Documents
- [README](./README.md)
## Metadata
- **Document Type:** index
- **Auto-generated:** This file is automatically generated by rebuild_indexes.py
## Revision History
| Change | Notes | Author |
| --- | --- | --- |
| Automated update | Generated by documentation index automation | rebuild_indexes.py |
+1 -1
View File
@@ -18,7 +18,7 @@ import { resolve } from 'node:path';
import { homedir } from 'node:os';
import type { JoomlaConfig, JoomlaConnection } from './types.js';
const CONFIG_FILENAME = '.joomla-api-mcp.json';
const CONFIG_FILENAME = '.mcp_mokowaas.json';
export async function loadConfig(): Promise<JoomlaConfig> {
const config_path = process.env.JOOMLA_API_MCP_CONFIG