Knowledge MCP
Explore private repository structure and use GitHub MCP tools.
Search, read, list, and semantically search files in Google Drive.
Inspect Railway services, deployments, and logs.
Search messages, read channels, list channels, and find files in Slack (read-only).
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Knowledge MCPsearch my Google Drive for the Q3 marketing report"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Quick start
Pick the path that matches what you want. Both start with no database to set up: state lives in a local SQLite file. You need Node 22.13 or later, or Docker.
You want | Start with |
Your own tools (internal APIs, databases, anything) |
|
Only the built-in connectors (Drive, Slack, GitHub, Railway) | The Docker image |
Your own tools
npx create-knowledge-mcp my-mcp
cd my-mcp
npm run dev # http://localhost:8080/mcp, reloads on save
npm run keys -- create --email you@example.com --role admin # prints an API key onceThe project comes with an example plugin, src/plugins/notes.ts. Point a client at
http://localhost:8080/mcp with Authorization: Bearer <key> and you will see notes_add and notes_list.
Only the built-in connectors
cp .env.example .env # fill in the connectors you want
cp mcp.config.example.json mcp.config.json # Drive folders, excluded folders, sign-in allowlist
docker run -d --name kmcp -p 8080:8080 -v kmcp-data:/data --env-file .env \
-v "$PWD/mcp.config.json:/app/mcp.config.json:ro" ghcr.io/chanthr/knowledge-mcp:latest
docker exec kmcp knowledge-mcp keys create --email you@example.com --role admin
docker exec kmcp knowledge-mcp doctor # which connectors are on, and what each one is missing.env.example lists every variable. Each connector turns on when its variables are set.
The image holds no config: mount mcp.config.json as above, or pass it inline with -e MCP_CONFIG_JSON='{…}'.
compose.yaml does the same with Docker Compose.
Related MCP server: brkt-gdrive-mcp
How it works
Part | What it does | You write it? |
Core | HTTP and stdio transports, API keys, OAuth 2.1 with Google sign-in, roles, audit log, secret masking in logs | No |
Plugins | One connector each: its tools, the env vars it needs, its permission group | Yes, one file per plugin |
Gateway | Proxies other MCP servers (GitHub's, Notion's, yours) under a name prefix, read-only by default | Config only |
Store | Keys, users, OAuth tokens, plugin state, audit log. SQLite file by default, Supabase optional | No |
Step | Who | What happens |
1 | Client | Calls |
2 | Core | Checks the key against its stored hash, or verifies the OAuth token, then loads the person's role and limits. |
3 | Core | Lists only tools whose plugin is configured and that the person's role allows. |
4 | Plugin | Gets validated input and the caller, does the work, returns text or JSON. The call is logged. |
Add a tool
Every file in src/plugins/ is a plugin. Copy the example and edit it:
// src/plugins/weather.ts
import { definePlugin, defineTool, ToolError, z } from '@chnxthr/knowledge-mcp';
export default definePlugin({
id: 'weather', // also the permission group
env: ['WEATHER_API_KEY'], // the tools stay hidden until this is set
tools: [
defineTool({
name: 'weather_now',
description: 'Current weather for a city. Use it for questions about today’s weather.',
input: z.object({ city: z.string().describe('City name, e.g. Seoul') }),
annotations: { readOnlyHint: true },
run: async ({ city }, ctx) => {
const r = await fetch(`https://api.example.com/weather?q=${encodeURIComponent(city)}`, {
headers: { Authorization: `Bearer ${process.env.WEATHER_API_KEY}` },
});
if (r.status === 404) throw new ToolError(`No city named ${city}`);
ctx.log(`weather for ${city} by ${ctx.user.email}`);
return r.json(); // any JSON value is sent as pretty JSON
},
}),
],
});Save the file and the dev server restarts. New files in src/plugins/ are picked up too. You did not touch a
router, a tool list or a permission table.
The core does this for you | How |
Lists the tool | From |
Hides it when not configured | Until every name in |
Validates input | Bad input returns |
Checks permission | The plugin |
Formats the result | A string becomes text, any other value becomes pretty JSON, an MCP result passes through |
Handles errors |
|
Logs the call | Tool, person, role, success and duration go to the audit log. Arguments are not stored |
Plugin API
Everything is exported from @chnxthr/knowledge-mcp.
Field | Type | Meaning |
|
| Lowercase letters, digits and dashes. Permission group and config key |
|
| One line shown by |
|
| Env vars that must all be set for the plugin to turn on |
|
| Extra condition. Return a reason string to stay off; |
|
| Tools made with |
|
| Remote MCP servers whose tools join this plugin's group (see Gateway) |
|
| Usage notes added to what the client receives on connect |
| zod schema | Validates |
|
| Runs once after the HTTP server listens. For background jobs. Not run in stdio mode |
|
| CLI commands: |
Field | Type | Meaning |
|
| Lowercase letters, digits and underscores. Unique across all plugins; |
|
| What it does and when to use it. The model picks tools by this |
| zod object | Arguments. Describe each field with |
|
| MCP hints such as |
|
| Extra condition for this one tool |
|
| Gets the parsed input and the context below |
| Meaning |
|
|
|
|
| This plugin's |
| Writes to stderr with the plugin name. Secrets are masked. Never use |
| The full auth context, including Slack channel and Drive folder limits |
A generated project has one entry file. It is also the command line, so every command sees your plugins.
import { run, builtinPlugins, loadPlugins } from '@chnxthr/knowledge-mcp';
const mine = await loadPlugins(new URL('./plugins/', import.meta.url));
await run({ plugins: [...builtinPlugins, ...mine] });loadPlugins imports every .ts and .js file in the folder in name order. It skips names starting with _,
.d.ts files and tests. Leave out builtinPlugins, or pick from drivePlugin, slackPlugin, githubPlugin
and railwayPlugin, to ship fewer connectors.
CLI
The knowledge-mcp binary, the Docker image entrypoint, and your own server.ts all take these commands.
Command | What it does |
| Starts the HTTP server on |
| Creates an API key and prints it once. Only its hash is stored |
| Lists keys (never the key itself) or deactivates one |
| Registers a person. |
| Shows or removes people |
| Config source, store, OAuth, and each plugin on or off with the reason |
| A plugin command, e.g. |
Built-in plugins
Each turns on when its env vars are set. All built-in tools only read.
Plugin | Tools | Needs |
|
| A Google service account or OAuth refresh token, and |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core |
| Nothing. Shows who you are and which plugins are on |
Task | How |
First full index |
|
Keep it fresh | The HTTP server syncs Drive changes every 30 minutes ( |
Clean up | A full run deletes vectors of files no longer listed, such as files moved into an excluded folder |
Start over |
|
Format | How text is extracted |
Markdown, plain text | As is |
Google Docs, Slides | Drive export |
Google Sheets | Drive export as CSV. Only the first sheet |
Word, PowerPoint, Excel, PDF with text | Local parsers |
Scanned PDF, PNG, JPEG, WebP | Mathpix OCR, only when |
Files larger than DRIVE_MAX_FILE_BYTES (50 MB by default) are not read or indexed. Embeddings use OpenAI
text-embedding-3-small.
slack_list_channels tells the model what each channel is for.
Invite the bot to the channels it should read, then call
slack_list_channelsonce from a client. That call copies the channel list from Slack into the map. New channels arrive switched off.Describe a channel and switch it on:
knowledge-mcp slack channel '#dev' --purpose "Deploys and incidents" --on. List the map withknowledge-mcp slack channels.
On and off only decide what slack_list_channels shows by default. They do not grant or block reading:
that depends on the bot's channels and on each person's allowed channels.
Configuration
Layer | Where | What |
Secrets | Env vars ( | Keys and tokens. |
What to look at |
| Drive folders, exclusions, sign-in allowlist, viewer access, plugin settings, remote MCP servers. See |
.env and mcp.config.json are read from the working directory first, then from your project folder, so a
client that starts the server from elsewhere still finds them. MCP_CONFIG_JSON (inline) and
MCP_CONFIG_PATH override the file. The config is read once at startup: restart the server after you change
it, the sign-in allowlist included.
Config key | Meaning |
| Organization identifier carried in the auth context |
| Name shown on the sign-in consent page and in OAuth metadata. |
|
|
| Folder names skipped everywhere: index, search, read and list. Case and spacing are ignored |
| Top-level |
| Optional |
| Vector collection name (env |
| Who may sign in with Google |
| Plugin ids open to signed-in people without a users entry. Default: none |
| Settings for one plugin, passed as |
| Remote MCP servers (next section) |
| Supabase schema for the server's tables (Supabase store only) |
Store | When | Setup |
SQLite (default) | One server, one machine | Nothing. The file is |
Supabase | State in a shared Postgres you can browse | Run |
Setting both Supabase variables selects Supabase. MCP_STORE=sqlite|supabase forces one.
Remote MCP servers
The server can also be a gateway: list another MCP server and its tools appear under a prefix, behind your keys and permissions.
{
"upstreams": [
{
"name": "notion",
"url": "https://mcp.example.com/mcp",
"tokenEnv": "NOTION_MCP_TOKEN",
"expose": "read"
}
]
}Field | Meaning |
| Permission group and log name |
| Tool name prefix. Default |
| Env var holding the bearer token. The token never goes in the config file |
|
|
If a remote tool has the same name as a local one, the local tool wins and the remote one is hidden.
Access control
Who someone is comes from an API key or a Google sign-in. What they can use comes from their users entry.
Users entry | Role | What they can use |
| admin | Every tool |
| member | Every plugin, unless |
No entry | viewer | Only |
knowledge-mcp users add lee@example.com --role member --tools drive,notes
knowledge-mcp keys create --email lee@example.com --expires 90dKeys look like
kmcp_…and are stored only as SHA-256 hashes. The key is shown once.A key with an email takes the person's role from their users entry, so removing the entry demotes the key. A key without an email is a service key with its own role.
keys revoke <id>takes effect withinAUTH_CACHE_TTL_MS(60 seconds by default).Failed authentication is rate-limited per client IP. Set
TRUST_PROXYto match your proxy so the IP is real.stdio runs as the local admin unless
MCP_API_KEYis set, because whoever starts the process already holds every token in its env.
Set the OAuth variables and an allowlist to turn on a built-in OAuth 2.1 server that signs people in with
Google. It shows a consent page naming the app and where the token goes before anything is issued. The full
guide is OAUTH_SETUP.md.
Deploy
What | How |
Built-in connectors |
|
Your own project |
|
Railway |
|
HTTPS. Put the server behind HTTPS before remote clients connect. OAuth needs an
httpsissuer.One replica. Sessions and pending sign-ins live in memory, and SQLite is one file. Run a single instance.
Keep
/data. Without a volume, keys and users are lost on redeploy.
Connect a client
Client | How |
Claude Code, local |
|
Claude Code, remote |
|
Claude web and desktop | Turn on OAuth, then add a custom connector with |
Any MCP client | URL |
Upgrading from 0.2
Change | What to do |
Supabase is optional; SQLite is the default | Existing installs with |
Commands moved into the CLI |
|
Role | Existing rows keep working. |
stdio without | Runs as the local admin instead of refusing to start |
Broken | Now stops the server with the parse error instead of silently using defaults |
Security notes
Never commit
.env, service-account JSON,mcp.config.jsonordata/. All are gitignored and kept out of the image.The data file holds key hashes and OAuth client secrets. It is created owner-only; keep the volume private.
Built-in plugins never change Drive, Slack, GitHub or Railway. The gateway exposes remote write tools only if you set
expose.Drive indexing sends document text to OpenAI for embeddings, and scanned files to Mathpix when OCR is on.
A token reaches whatever it can read. Scope the Slack bot, the GitHub token and the Railway token to what every user may see.
File map
Path | Contents |
Plugin API: | |
Auth, OAuth, permissions, config, gateway, log masking | |
HTTP and stdio transports, MCP request handling, identity | |
Store interface, SQLite and Supabase implementations | |
Built-in plugins: | |
The command line | |
Unit tests and end-to-end tests over HTTP and stdio | |
The generator and its project template | |
Pack, scaffold, install, build, serve and call, end to end | |
Image, local stack, Railway | |
CI, image and npm workflows, issue and PR templates, README images ( |
Develop with npm install, npm test (unit and end-to-end), npm run typecheck and npm run smoke:create.
Contributing
Please open an issue for problems and ideas.
Template | Use it when |
Something behaves differently from the code or the docs | |
You have a better way, or a doc no longer matches the code |
This repository is public. Leave API keys, tokens, internal URLs and private documents out of issues and pull requests. Report security problems privately with Report a vulnerability on the Security tab, not in an issue.
License
MIT © chanthr
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Private persistent memory for Claude, ChatGPT & Gemini via MCP - semantic search, zero-code setup.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI applications to access and contextualize organizational knowledge sources including GitHub repositories and internal documentation through standardized MCP protocol integration. Features OAuth 2.1 authentication, vector-based semantic search, and optimized context chunking for enterprise development workflows.-
- AlicenseNot gradedqualityDmaintenanceMCP server for semantic search over Google Drive documents, enabling AI tools to search, list, and retrieve document content.MIT
- AlicenseAqualityBmaintenanceAn MCP server that enables LLMs to search, fetch, and act on Google Workspace (Drive, Gmail, Docs, Sheets, etc.) with rich, one-call results and file deposits to disk, reducing context usage.3MIT
- AlicenseAqualityBmaintenanceExposes internal databases, HTTP APIs, and files to LLM agents over MCP, with auto-generated schemas, access controls, rate limiting, redaction, and audit logging.3MIT