Compare commits
2 Commits
v1.1.0-alp
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 110f87e53f | |||
| f48dafc88f |
50
README.md
50
README.md
@@ -149,6 +149,7 @@ Add to your MCP client configuration (e.g., Claude Desktop):
|
|||||||
| `PORT` | Server port | `3000` |
|
| `PORT` | Server port | `3000` |
|
||||||
| `GRIST_MCP_TOKEN` | Agent authentication token (required) | - |
|
| `GRIST_MCP_TOKEN` | Agent authentication token (required) | - |
|
||||||
| `CONFIG_PATH` | Path to config file inside container | `/app/config.yaml` |
|
| `CONFIG_PATH` | Path to config file inside container | `/app/config.yaml` |
|
||||||
|
| `LOG_LEVEL` | Logging verbosity (`DEBUG`, `INFO`, `WARNING`, `ERROR`) | `INFO` |
|
||||||
|
|
||||||
### config.yaml Structure
|
### config.yaml Structure
|
||||||
|
|
||||||
@@ -188,6 +189,55 @@ tokens:
|
|||||||
- `write`: Add, update, delete records
|
- `write`: Add, update, delete records
|
||||||
- `schema`: Create tables, add/modify/delete columns
|
- `schema`: Create tables, add/modify/delete columns
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
Set the `LOG_LEVEL` environment variable to control logging verbosity:
|
||||||
|
|
||||||
|
| Level | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `DEBUG` | Show all logs including HTTP requests and tool arguments |
|
||||||
|
| `INFO` | Show tool calls with stats (default) |
|
||||||
|
| `WARNING` | Show only auth errors and warnings |
|
||||||
|
| `ERROR` | Show only errors |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In .env or docker-compose.yml
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Format
|
||||||
|
|
||||||
|
At `INFO` level, each tool call produces a single log line:
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-01-02 10:15:23 | agent-name (abc...xyz) | get_records | sales | 42 records | success | 125ms
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| Timestamp | `YYYY-MM-DD HH:MM:SS` |
|
||||||
|
| Agent | Agent name with truncated token |
|
||||||
|
| Tool | MCP tool name |
|
||||||
|
| Document | Document name (or `-` for list_documents) |
|
||||||
|
| Stats | Operation result (e.g., `42 records`, `3 tables`) |
|
||||||
|
| Status | `success`, `auth_error`, or `error` |
|
||||||
|
| Duration | Execution time in milliseconds |
|
||||||
|
|
||||||
|
Errors include details on a second indented line:
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-01-02 10:15:23 | agent-name (abc...xyz) | add_records | sales | - | error | 89ms
|
||||||
|
Grist API error: Invalid column 'foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Recommendations
|
||||||
|
|
||||||
|
- Use `LOG_LEVEL=INFO` for normal operation (default)
|
||||||
|
- Use `LOG_LEVEL=DEBUG` for troubleshooting (shows HTTP traffic)
|
||||||
|
- Use `LOG_LEVEL=WARNING` for minimal logging
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
- **Token-based auth**: Each agent has a unique token with specific document access
|
- **Token-based auth**: Each agent has a unique token with specific document access
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ def setup_logging() -> None:
|
|||||||
|
|
||||||
logger = logging.getLogger("grist_mcp")
|
logger = logging.getLogger("grist_mcp")
|
||||||
logger.setLevel(level)
|
logger.setLevel(level)
|
||||||
|
logger.propagate = False # Prevent duplicate logs to root logger
|
||||||
|
|
||||||
# Only add handler if not already configured
|
# Only add handler if not already configured
|
||||||
if not logger.handlers:
|
if not logger.handlers:
|
||||||
|
|||||||
@@ -191,14 +191,16 @@ def _print_mcp_config(external_port: int, tokens: list) -> None:
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
class HealthCheckFilter(logging.Filter):
|
class UvicornAccessFilter(logging.Filter):
|
||||||
"""Suppress health check requests unless LOG_LEVEL is DEBUG."""
|
"""Suppress uvicorn access logs unless LOG_LEVEL is DEBUG.
|
||||||
|
|
||||||
|
At INFO level, only grist_mcp tool logs are shown.
|
||||||
|
At DEBUG level, all HTTP requests are visible.
|
||||||
|
"""
|
||||||
|
|
||||||
def filter(self, record: logging.LogRecord) -> bool:
|
def filter(self, record: logging.LogRecord) -> bool:
|
||||||
if "/health" in record.getMessage():
|
# Only show uvicorn access logs at DEBUG level
|
||||||
# Only show health checks at DEBUG level
|
return os.environ.get("LOG_LEVEL", "INFO").upper() == "DEBUG"
|
||||||
return os.environ.get("LOG_LEVEL", "INFO").upper() == "DEBUG"
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -209,8 +211,8 @@ def main():
|
|||||||
|
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
# Add health check filter to uvicorn access logger
|
# Suppress uvicorn access logs at INFO level (only show tool logs)
|
||||||
logging.getLogger("uvicorn.access").addFilter(HealthCheckFilter())
|
logging.getLogger("uvicorn.access").addFilter(UvicornAccessFilter())
|
||||||
|
|
||||||
if not _ensure_config(config_path):
|
if not _ensure_config(config_path):
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user