skgate
Proxies Google Gemini models through an OpenAI-compatible API, enabling apps to use Gemini via skgate.
Proxies a local Ollama instance through an OpenAI-compatible API, enabling apps to use Ollama models via skgate.
Proxies OpenAI's API and exposes it through skgate's OpenAI-compatible gateway, supporting model listing and chat completions.
Uses an external OpenID Connect/OIDC provider for admin authentication and OAuth 2.1 protection of MCP endpoints.

Name Origin
skgate /ɛsˈkɑːɡeɪt/ (ess-KAH-gate)
"sk" is how a lot of SI API keys start, and "gate" is for gateway. Not a great name, but it's ours.
The plane in the logo is an inside joke. The public wouldn't understand it, and I'm not about to explain it. Sorry.
Related MCP server: McpMux
Quality
Yes
Quick start
You need an OIDC provider with a confidential client for skgate. Admin login is OIDC only. Trying it on one machine? docs/quickstart.md runs skgate with a bundled provider and no accounts.
# docker-compose.yml
services:
skgate:
container_name: skgate
image: ghcr.io/helv-io/skgate:latest
restart: always
ports:
- 8080:8080
environment:
- PUBLIC_URL=https://skgate.example.com
- OIDC_ISSUER=https://auth.example.com
- OIDC_CLIENT_ID=skgate
- OIDC_CLIENT_SECRET=change-me
volumes:
- ./data:/data
healthcheck:
test: ["CMD", "/skgate", "healthcheck"]
interval: 30s
timeout: 5s
retries: 3docker compose up -dOpen
https://skgate.example.com/adminand sign in through your OIDC provider.mcp upstreams > Add upstream: a remote MCP server, or a GitHub repository, npm package, or PyPI package that skgate runs for you (see Add an MCP server with Suggest configuration).
Point your MCP client at
https://skgate.example.com/mcpor the site root for every included upstream, or athttps://skgate.example.com/mcp/<alias>for one upstream. Sign in when it asks.
Image tags:
latest: proxy + managed MCP servers. Bundles Node.js (npx), pnpm, Bun (bunx), Deno, Python (uv,uvx), Go, .NET and git.slim: proxy only
To upgrade, back up ./data, then docker compose pull && docker compose up -d.
Proxy your SI providers
skgate is also an OpenAI-compatible API in front of your SI providers. Point your apps at one address; switch the provider behind them without changing the apps. User-defined model aliases give your apps a stable model name of your choosing.
Grok can use subscription sign-in with no API key. Other providers use that provider's API key (OpenAI, Anthropic, Gemini, Mistral, DeepSeek, Groq, OpenRouter, Ollama, LM Studio, and any OpenAI-compatible endpoint).
status > Grok > Sign in: open the shown address, enter the code, approve. skgate keeps the sign-in fresh.
keys > enter a name > Create key. Copy the
sk-...key; it is shown once.Use it: base URL
https://skgate.example.com/v1, API keysk-...(see Examples).These base URLs all reach the same API:
/v1,/api,/api/v1, and the bare host. Use whichever form your client expects.
Other providers: status > Add provider, pick one and enter its API key. Type the provider's address in any form; skgate finds the one that works.
Examples
export SKGATE=https://skgate.example.com KEY=sk-...
curl -s $SKGATE/v1/models -H "Authorization: Bearer $KEY"
curl -s $SKGATE/v1/chat/completions -H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"<id from /v1/models>","messages":[{"role":"user","content":"Say hi"}]}'export OPENAI_BASE_URL=https://skgate.example.com/v1 OPENAI_API_KEY=sk-...from openai import OpenAI
client = OpenAI() # reads the two variables above
r = client.chat.completions.create(model="<id from /v1/models>", messages=[{"role": "user", "content": "Say hi"}])
print(r.choices[0].message.content)Aliases are user-defined: skgate ships with none. Pick any name and point it at a model. This example uses grok-latest, but the name is up to you.
Change the target in this one place and every app using that name switches at once, with no client config changes.
Grok > Aliases: New alias grok-latest, Target model grok-4.7, Add alias.
client sends {"model": "grok-latest", ...}
skgate sends {"model": "grok-4.7", ...}Aliases are listed first in /v1/models.
Needs the latest image and an MCP helper model from any ready provider. The first time, Pick MCP helper model next to the button opens the model picker right on the page.
mcp upstreams > Add upstream > Type managed process:
MCP source URL / package, one of:
Source
Runs as
@modelcontextprotocol/server-everythingnpm package,
npxpypi:mcp-server-timePyPI package,
uvxhttps://github.com/example-org/notes-mcpgit repo: clone, install, run (private: Suggest asks for a token)
Suggest configuration. skgate fetches the README and manifests (
package.json,pyproject.toml,server.json).The MCP helper model proposes the command, args, install step, and env names (secret or not, required or optional). The Manual configuration fields fill in, with a confidence and any warnings.
Nothing is saved yet. Set the variables the server needs.
Set an alias, fill in the variables you need (empty ones are not passed to the server), Save. The server is at
https://skgate.example.com/mcp/<alias>.
If the button is greyed out, hover it. Use Pick MCP helper model beside it, or add a provider on status first.
URL | Serves |
| One upstream |
| Every upstream marked In /mcp |
| Same aggregate as |
Hosted connectors use OAuth (leave client ID and secret empty). Scripts and CLIs send a key:
{"mcpServers": {"skgate": {"type": "http", "url": "https://skgate.example.com/mcp/<alias>",
"headers": {"Authorization": "Bearer sk-..."}}}}On a RAM-constrained homelab, idle MCP servers should cost nothing.
Managed servers (npx, pnpm dlx, bunx, deno, uvx, git) are child processes of skgate.
By default (Lifecycle on-demand) one starts on its first request and stops after 10 minutes without requests; the next request starts it again.
before 3 MCP servers = 3 containers, always running
after 1 skgate container; 0 server processes while idle, 1 per server in usestopped --request--> starting --> running --10 min idle--> stoppedDefault is on-demand; Lifecycle
always-onstarts the server at boot instead.The first request after a stop waits until the server is up.
A server with a request in flight is never stopped.
Idle time is
idleTimeoutSecondsin import JSON (default 600; not in the form):
{"mcpServers": {"time": {"command": "uvx", "args": ["mcp-server-time"], "skgate": {"idleTimeoutSeconds": 120}}}}The aggregated
/mcpincludes remote, OpenAPI and always-on upstreams marked In /mcp. On-demand servers are left out, so/mcpnever starts them. Point a client at/mcp/<alias>to use one.Remote and OpenAPI upstreams have no process; there is nothing to idle.
An admin Stop keeps a server stopped until Start or Restart.
mcp upstreams > import JSON > paste > Import. Needs the latest image.
{"mcpServers": {"everything": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-everything"]}}}Served at https://skgate.example.com/mcp/everything. The same package runs with any bundled runner:
Runner |
|
|
npm |
|
|
pnpm |
|
|
Bun |
|
|
Deno |
|
|
Python |
|
|
mcp upstreams > import JSON > paste > Import. skgate clones the repo, runs install, then the command.
{"mcpServers": {"notes": {"command": "node", "args": ["server.js"],
"skgate": {"gitUrl": "https://github.com/example-org/notes-mcp.git", "gitRef": "main", "install": "npm ci"}}}}mcp upstreams > Add upstream > Type remote server, or import:
{"mcpServers": {"docs": {"type": "http", "url": "https://mcp.example.com/mcp",
"headers": {"Authorization": "Bearer ..."}}}}Features
Feature | What it does |
Grok sign-in | Device code or browser paste-back; tokens refresh |
Other providers | OpenAI, Anthropic, Gemini and more by API key (docs) |
Model aliases | User-defined model names; change the target once |
API |
|
Virtual keys | Hashed; tokens in/out per key (docs) |
MCP | Remote, stdio and git servers behind OAuth 2.1 (docs) |
Live tool updates | Connected clients hear when an upstream's tools change after an update, a reinstall or a tool switched on or off, and pick up the new tools without reconnecting |
REST APIs as tools | Give an OpenAPI description, pick the operations (or let Suggest names and selection name them and pick a core set), and MCP clients get them as tools (docs) |
Tested MCP clients
Clients tested by the author against a live skgate. Status is what happened, not a promise.
Client | Status | Notes |
ChatGPT | OK | |
Claude | OK | |
Grok | OK | |
Hermes | OK | |
Open WebUI | OK | Use OAuth 2.1; register and authorize the client. |
Gemini | Warning | Connects, but never calls tools. |
Home Assistant | Warning | Needs a manual OAuth client ID and secret. Only one MCP upstream can be used. |
Cline | Untested | |
Continue | Untested | |
Cursor | OK | |
Goose | Untested | |
LibreChat | Untested | |
VS Code | Untested | |
Windsurf | Untested | |
Zed | Untested |
Directory mode
skgate stdio starts a small read-only MCP server on stdin and stdout. It exists so MCP directories and scanners, which start a server and ask what it offers, can look at skgate. It is not for real use: to run skgate, use the Docker image above.
It needs no settings, opens no port, reads no database and uses no network. It has three tools: about, list_features and config_help. They describe skgate and nothing else. More in docs/mcp.md.
How skgate compares
A rough guide from each project's docs as of 2026-10-13. Check them for current features.
skgate | |||
OpenAI-compatible LLM proxy | Yes | Yes, 100+ providers | No |
MCP gateway | Yes | Yes | Yes |
Runs stdio MCP servers | Yes, started on demand, stopped when idle | Yes, off unless | Yes |
OAuth for MCP clients | OAuth 2.1 with dynamic client registration | Yes, dynamic client registration at the gateway | Yes, MCP spec 2025-06-18 OAuth |
Database | SQLite, one container | Postgres for virtual keys | Postgres (Docker Compose) |
Comparison
How each provider signs in elsewhere, and how skgate connects to it:
Provider | Subscription sign-in elsewhere | In skgate |
xAI Grok * | OpenCode; more planned [1] | Subscription |
Anthropic Claude | Not offered to third parties [2] | API key |
DeepSeek | None | API key |
Google Gemini * | CLI login not for reuse [3] | API key |
Groq * | None | API key |
Mistral * | None | API key |
OpenAI | Sign in with ChatGPT [4] | API key |
OpenRouter * | None | API key |
LM Studio | None | No key (local) |
Ollama | None | No key (local) |
* Free tier available.
As of 2026-10-05; check each provider's terms. GitHub Copilot is not a skgate provider.
xAI, Use Grok in OpenCode
Anthropic, Legal and compliance
Google, Gemini CLI terms
OpenAI, Sign in with ChatGPT
Configuration
Set these under environment: or in an env_file. Placeholders are in .env.example.
Variable | Default | Purpose |
|
| Public origin, no trailing slash. |
| Issuer URL, equal to the provider's discovery | |
| Confidential client credentials. | |
|
| Requested scopes. |
|
| Callback registered at the provider. |
| empty | Comma lists limiting who is admin. |
|
| Approve/Deny page after login at |
| random | Encrypts stored upstream secrets. 32-byte base64 ( |
| empty | Optional GitHub token for Suggest when it reads a repository. An upstream's own access token takes precedence. Raises GitHub's rate limit. |
|
| The version in the header turns yellow when a newer release exists; |
|
| Listen address. |
|
| SQLite file. |
|
|
|
|
| Lines of output kept per managed process (its current and previous run, at most 512 KB). |
|
| Time zone for the UI and logs, for example |
|
| Run-as ids; never |
|
| Work dirs and clones of managed upstreams. |
|
| Concurrent managed processes; |
Grok needs no variables. Sign in on the status page. Model aliases are the Aliases control on that card.
Everything lives in /data (skgate.db, secrets.key): back up both.
Reverse proxy
Set PUBLIC_URL to the public https origin.
Do not put forward-auth on /v1, /api, /mcp, /sse, /messages, /authorize, /token, /register, or /.well-known.
Only Traefik is tested by the author. Open an issue with what you tried.
services:
skgate:
container_name: skgate
image: ghcr.io/helv-io/skgate:latest
restart: always
network_mode: web # existing Docker network shared with Traefik; no ports needed
environment:
- PUBLIC_URL=https://skgate.example.com # the public https origin
- OIDC_ISSUER=https://auth.example.com
- OIDC_CLIENT_ID=skgate
- OIDC_CLIENT_SECRET=change-me
volumes:
- ./data:/data
healthcheck:
test: ["CMD", "/skgate", "healthcheck"]
interval: 30s
timeout: 5s
retries: 3
labels:
# no forward-auth middleware on /v1, /api, /mcp, /sse, /messages, /authorize, /token, /register, /.well-known
- traefik.enable=true
- traefik.http.routers.skgate.rule=Host(`skgate.example.com`)
- traefik.http.routers.skgate.entryPoints=websecure
- traefik.http.services.skgate.loadbalancer.server.port=8080server {
listen 443 ssl;
http2 on;
server_name skgate.example.com;
ssl_certificate /etc/ssl/skgate/fullchain.pem;
ssl_certificate_key /etc/ssl/skgate/privkey.pem;
client_max_body_size 32m; # skgate accepts up to 32 MB
location / {
proxy_pass http://skgate:8080;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host; # keep Host
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off; # SSE streaming
proxy_read_timeout 3600s; # long streams
proxy_send_timeout 3600s;
}
}skgate.example.com {
# Host and X-Forwarded-* are set by default; no body limit, no response timeout
reverse_proxy skgate:8080 {
flush_interval -1 # SSE streaming
}
}defaults
mode http
timeout connect 5s
timeout client 1h # long streams
timeout server 1h
timeout tunnel 1h
frontend https
bind :443 ssl crt /etc/haproxy/certs/skgate.pem alpn h2,http/1.1
option forwardfor # X-Forwarded-For; Host is kept, responses are not buffered
http-request set-header X-Forwarded-Proto https
default_backend skgate
backend skgate
option httpchk GET /healthz
server skgate skgate:8080 check# a2enmod ssl proxy proxy_http headers
<VirtualHost *:443>
ServerName skgate.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/skgate/fullchain.pem
SSLCertificateKeyFile /etc/ssl/skgate/privkey.pem
# keep Host
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
# long streams
ProxyTimeout 3600
# flushpackets: no buffering (SSE)
ProxyPass / http://skgate:8080/ flushpackets=on
ProxyPassReverse / http://skgate:8080/
</VirtualHost>OIDC setup
Client setting | Value |
Type | Confidential, |
Flow | Authorization code with PKCE S256 |
Redirect URI |
|
Scopes |
|
ID token | Asymmetric signature (RS, PS, ES, EdDSA); discovery |
Without OIDC_ISSUER, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET, skgate starts but the admin stays closed.
Every user your provider lets in is an admin. Restrict the provider, or set OIDC_ALLOWED_EMAILS / OIDC_ALLOWED_GROUPS.
Hints for Authelia, Authentik, Keycloak, Zitadel and Pocket ID: docs/oidc.md.
Security notes
Virtual keys are stored as SHA-256 hashes; upstream credentials are AES-256-GCM encrypted.
/authorizeneeds an admin session.Managed upstreams run admin-supplied commands; use the
slimimage to disable them./register,/token,/authorizeand admin forms cut off a request body that arrives too slowly. Uploads and streams (/v1,/mcp) have no such limit.A key can be allowed in the URL (
?key=) for clients that cannot send headers. It is off per key by default, because URLs leak into logs, history and referrers.
More, including the known trade-offs: operations.
Built with SI assistance
skgate is built with SI assistance: coding agents write much of the code, tests and docs.
Every change is reviewed by the author, and the security paths (OAuth, tokens, secrets) are human reviewed and tested. Reviews by frontier models are a supplement, not a substitute. Issues and pull requests that put it to the test are welcome.
Development
go build ./... && go vet ./... && go test ./...docs/development.md. Contributors and agents: AGENT.md.
License
MIT, see LICENSE.
Star History
Available Tools
3 toolsaboutAbout skgateARead-onlyIdempotent
Describe skgate: what it is, the running version, the edition, the licence and where its source and docs live. Takes no arguments. Returns one object with name, version, edition, summary, license, repository, docs and mode. Read-only and offline: it answers from the binary alone and contacts nothing. Call it first to learn what skgate is before using list_features or config_help.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| docs | Yes | Address of the documentation folder. |
| mode | Yes | States that this is the read-only directory mode and not the gateway. |
| name | Yes | Always skgate. |
| edition | Yes | full runs MCP servers for you, slim only proxies. |
| license | Yes | Licence of the source code. |
| summary | Yes | One-paragraph description of skgate. |
| version | Yes | Release number, for example 0.27.3. |
| repository | Yes | Address of the source code. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/closed-world safety, but the description adds genuinely new context: 'Read-only and offline: it answers from the binary alone and contacts nothing' clarifies there is no network dependency or side effect. It also enumerates the returned fields, though the output schema already documents the shape.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the verb and scope, followed by argument behavior, return shape, and the routing hint. Three tight sentences with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a zero-param tool with full schema coverage, existing annotations, and an output schema, the description supplies everything else an agent needs: what it answers, that it is offline/read-only, and when to call it relative to siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so per calibration the baseline is 4. The description confirms 'Takes no arguments', which matches the empty schema and removes any ambiguity about hidden inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Describe skgate') and enumerates exactly what it covers: what it is, running version, edition, licence, source and docs locations. An agent can distinguish it from list_features and config_help (both named) without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs 'Call it first to learn what skgate is before using list_features or config_help', naming both alternatives and the ordering condition that selects this tool. No inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
config_helpExplain skgate settingsARead-onlyIdempotent
Explain the environment variables that configure a skgate server. Takes an optional variable, an exact variable name such as PUBLIC_URL (case does not matter); without it the tool returns every variable. Returns an object with a settings array of name, default and description, in the order of the README table. Defaults are the documented values and no secret is ever included. An unknown name is an error that lists the valid names. Read-only and offline: it shows documented defaults, reads no environment variable and cannot tell you the values of a running server.
| Name | Required | Description | Default |
|---|---|---|---|
| variable | No | Exact environment variable name, for example OIDC_ISSUER. Omit it for all variables. |
Output Schema
| Name | Required | Description |
|---|---|---|
| settings | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, closed-world behavior, and the description adds real value beyond them: defaults are documented README values, no secret is ever included, result ordering follows the README table, unknown-name errors enumerate valid names, and it reads no environment variable at all. That is exactly the kind of edge-case disclosure annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Roughly four dense sentences, front-loaded with purpose and scope, then error behavior, then the read-only/offline caveat. No filler; each clause carries a distinct fact an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A one-parameter read tool with full annotation coverage and an output schema, yet the description still describes the returned shape (settings array of name/default/description), the ordering, and the error path. Nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description adds genuine semantics on top: the name is an exact match and case-insensitive, and omission returns the full set. The case-insensitivity point in particular is not derivable from the schema, which implies a strict-exact match.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Explain the environment variables that configure a skgate server.' The scope is narrow enough that an agent can separate it from siblings like 'about' and 'list_features' without opening the schema, and it explicitly names the optional target (a variable name).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the selection condition clearly: pass an exact variable name to narrow scope, omit it to get everything, and an unknown name raises an error listing valid names. It also gives an implicit 'when not to use' by noting it cannot report values from a running server. It does not, however, name any sibling tool as the alternative for live server values.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_featuresList skgate featuresARead-onlyIdempotent
List what skgate does, one entry per feature, optionally limited to one area. Takes an optional area: mcp (MCP servers and tools), openapi (REST APIs as tools), llm_proxy (the OpenAI-compatible API, providers, aliases and keys), auth (OAuth and sign-in) or deploy (running and configuring it). Without an area it returns every feature. Returns an object with the area asked for ('all' by default) and a features array of id, area and summary. An unknown area is an error that names the valid ones. Read-only and offline.
| Name | Required | Description | Default |
|---|---|---|---|
| area | No | Limit the list to one area. Omit it for all features. |
Output Schema
| Name | Required | Description |
|---|---|---|
| area | Yes | The area that was asked for, or all. |
| features | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, openWorldHint=false and destructiveHint=false, so "Read-only and offline" is largely redundant. The description does add non-obvious behavior beyond the annotations: the error semantics for an unrecognized area and the exact shape of the returned object.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and the default behavior ("Without an area it returns every feature"), then the return shape, then the error case. Dense but every sentence carries information; the enum parenthetical is long but earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need not be spelled out, yet the description still summarizes them, and it covers the default, the enum meanings, and the error path. For a zero-required-parameter read tool this is essentially complete, with only sibling disambiguation missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum is already documented, but the description goes further by explaining what each enum value actually covers (mcp = MCP servers and tools, llm_proxy = OpenAI-compatible API, providers, aliases and keys, etc.). That is real added meaning an agent cannot get from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("List what skgate does, one entry per feature") and immediately scopes it with the optional area filter. It is clear what the tool returns, but it never distinguishes itself from the sibling tools 'about' and 'config_help', which plausibly also describe product information.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear when-to-use guidance for the one decision point: omit the area to get everything, pass an area to narrow. It also documents the failure mode (unknown area is an error naming valid values). It stops short of routing the agent away from the overlapping siblings about/config_help.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
about - First observed
config_help - First observed
list_features
TDQS
Scored across 3 tools
All three tools are read-only introspection, but they target distinct surfaces: product metadata (about), feature inventory (list_features), and environment variables (config_help). 'about' and 'list_features' are the closest pair since about returns a summary and mode while list_features returns feature entries, but the descriptions draw a workable line and about is explicitly an entry point.
The names mix conventions: 'about' is a bare preposition/noun, 'list_features' is verb_noun, and 'config_help' is noun_noun. All are lowercase snake-ish and readable, but there is no predictable pattern an agent could extrapolate from.
Three tools is thin for a server whose whole purpose is explaining a product, though each is scoped to a different information source with no redundancy. It sits at the borderline rather than being clearly under- or over-provisioned.
The surface is deliberately offline and binary-only, so it cannot read live environment values, enumerate configured MCP servers/tools, or validate an actual running config. Documentation lookup is covered, but agents that need to inspect or verify a running skgate instance hit a dead end.
Maintenance
Related MCP Connectors
Managed MCP gateway with OAuth, per-agent permissions, shared accounts and activity logs.
Skills, agents and memory in the cloud, plus a gateway to your other MCP servers.
One connector for 15,000+ MCP servers plus your team's private MCPs, from any AI client.
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA unified hub for centrally managing and dynamically orchestrating multiple MCP servers/APIs into separate endpoints with flexible routing strategies.775 npm2,587Apache 2.0
- AlicenseNot gradedqualityAmaintenanceMcpMux is a desktop app that runs a local MCP gateway. You configure servers once — every AI client connects through a single URL.23 npm25GPL 3.0
- AlicenseAqualityBmaintenanceGrok asked for MCP on a URL. This is it. First MCP server built for Grok URL-based • Zero config • Just works12791 npm19MIT
- AlicenseNot gradedqualityDmaintenanceA Node.js MCP server with custom OAuth 2.1 + PKCE authentication, enabling secure remote connections to LLMs like Claude and ChatGPT.432 npmMIT