My MCP Servers for Productivity
Each file in this repo is a single-file, stdio-transport MCP server. Most of them are standard-library only; a few need one extra pip package. Install into the SAME Python interpreter that your MCP client (e.g. Continue) will launch the server with.
| Server | pip install | Notes |
|---|---|---|
confluence.py |
none | standard library only |
knowledge-base.py |
none | standard library only (keyword search) |
knowledge-base-semantic.py |
none | standard library only; meaning-based retrieval driven by your connected model (no local embedding model, no network) |
ms-excel.py |
none | standard library only (parses .xlsx as a zip of XML) |
ms-word.py |
pip install python-docx |
also pulls in lxml (compiled) and typing_extensions |
ms-outlook.py |
pip install pywin32 |
Windows only (COM automation of classic Outlook) |
pdf-to-md.py |
pip install pymupdf pymupdf4llm |
OCR of scanned PDFs additionally requires Tesseract installed on the machine (not a pip package) |
pip install python-docx pymupdf pymupdf4llm pywin32
(Drop pywin32 if you're not on Windows / not using ms-outlook.py.)
ms-word.pyandpdf-to-md.pylogsys.executableon startup, so if a dependency reports as "missing" even after installing it, check that you installed into the same interpreter your MCP client launches the server with, e.g.:"C:\path\to\python.exe" -m pip install python-docx pymupdf pymupdf4llm pywin32- For airgapped/offline installs, see the docstring at the top of
ms-word.pyfor the wheel-sideloading steps (same pattern applies topdf-to-md.py's dependencies).
All servers speak MCP over stdio. Add each one as an entry under the
mcpServers: block of Continue's config.yaml. Use the full path to the
Python interpreter you installed dependencies into (not a bare python),
and set PYTHONUTF8: "1" so Windows' default codepage can't corrupt the
stdio JSON stream. After editing config.yaml, use VSCode's
"Developer: Reload Window" rather than toggling the server, to avoid a
known "already connected to transport" reconnection bug.
Configuration is via environment variables (or the equivalent CLI flags, which take priority over the env vars).
| Env var | CLI flag | Purpose |
|---|---|---|
CONFLUENCE_BASE_URL |
--base-url |
Base URL incl. any context path, no trailing slash |
CONFLUENCE_TOKEN |
--token |
Personal Access Token, sent as Bearer (preferred over basic auth) |
CONFLUENCE_USER |
--user |
Username for basic auth (fallback if no token) |
CONFLUENCE_PASSWORD |
--password |
Password for basic auth |
CONFLUENCE_CA_CERT |
--ca-cert |
Path to a PEM CA bundle for an internal CA |
CONFLUENCE_VERIFY_SSL=false |
--insecure |
Disable TLS certificate verification |
CONFLUENCE_TIMEOUT |
--timeout |
Request timeout in seconds (default 30) |
CONFLUENCE_MAX_BODY |
--max-body |
Truncate page bodies to N chars, 0 = unlimited (default). Applies only to text returned to the model, not to files saved via CONFLUENCE_KB_DIR |
CONFLUENCE_KB_DIR |
--kb-dir |
If set, every page read is also saved as a Markdown file (Confluence - <title>.md, overwritten each time) into this folder — handy for feeding a local RAG knowledge base, e.g. alongside knowledge-base.py |
mcpServers:
- name: confluence
command: C:\path\to\python.exe
args:
- C:\path\to\confluence.py
- --max-body
- "20000"
- --kb-dir
- C:\reference-docs\confluence
env:
CONFLUENCE_BASE_URL: https://confluence.internal.example.com
CONFLUENCE_TOKEN: your-personal-access-token
PYTHONUTF8: "1"| CLI flag | Purpose |
|---|---|
--docs-dir |
Folder of reference docs to expose (.md/.markdown/.txt), searched recursively. Falls back to REFERENCE_DOCS_DIR env var |
--check |
List the folder's documents to stderr, then exit (no server) |
--version |
Print version and exit |
mcpServers:
- name: reference
command: python
args:
- C:\path\to\knowledge-base.py
- --docs-dir
- C:\reference-docs
env:
PYTHONUTF8: "1"For meaning-based (rather than keyword) retrieval over the same kind of folder, see
knowledge-base-semantic.pybelow. You can run both at once (differentname:and folder).
Meaning-based retrieval over a folder of your own markdown files (notes, policies, reference docs). Ask a question in your own words — e.g. "can I extend my work trip by two days, pay my own weekend accommodation, and fly back Monday?" — and the agent finds the relevant part of your travel policy and reasons over it, without you naming the file.
How it stays "semantic" with no embedding model and no network: MCP has no
way for a server to ask the client for embeddings, and Continue does not
support MCP sampling (server-initiated LLM calls), so the server cannot call
your model itself. Instead it hands the agent — which is the model you
connect to — a compact, structure-aware map of the knowledge base
(kb_outline: every document + its section headings + a one-line lead). The
agent picks the right document/section by meaning, then reads exactly that
section (kb_read). Retrieval by meaning, using the model you already talk to,
with zero dependencies. (If you ever want true vector embeddings, that requires
either a local embedding model or a remote embeddings API — neither of which
this offline-friendly server uses.)
| Tool | Purpose |
|---|---|
kb_list |
List every document with its title (inventory) |
kb_outline |
The map: every document's heading tree + a lead line per section — call this first for a topic question, then pick by meaning |
kb_search |
Keyword search across all docs; reports the matching section heading and a snippet (fast lookup / narrowing a large KB) |
kb_read |
Read one document in full, or just one named section of it (loose name/heading matching) |
| CLI flag | Purpose |
|---|---|
--docs-dir |
Folder of markdown/text docs to expose (.md/.markdown/.txt), searched recursively. Falls back to the KB_DOCS_DIR env var |
--check |
List the folder's documents (and a heading count each) to stderr, then exit (no server) |
--version |
Print version and exit |
mcpServers:
- name: kb
command: C:\path\to\python.exe
args:
- C:\path\to\knowledge-base-semantic.py
- --docs-dir
- C:\Users\me\knowledge-base
env:
PYTHONUTF8: "1"| CLI flag | Purpose |
|---|---|
--folder |
Folder of .xlsx/.xlsm workbooks to expose (default set in the CONFIG block in the file) |
--check |
Print environment/config diagnostics and exit (no server) |
--list |
List readable workbooks in the folder and exit (no server) |
mcpServers:
- name: excel
command: C:\path\to\python.exe
args:
- C:\path\to\ms-excel.py
- --folder
- C:\path\to\your\workbooks
env:
PYTHONUTF8: "1"Windows only — requires classic Win32 Outlook (not "New Outlook") installed, running, and logged into a profile.
| CLI flag | Purpose |
|---|---|
--blacklist-file |
Path to a file of extra content-blacklist terms (one per line, # for comments), added to the built-in list |
--search-folders |
Comma-separated folder names used as the default folder set for outlook_search_recent, overriding the SEARCH_ALL_FOLDERS value in the file (e.g. "Inbox,Sent Items,Archive"). A per-call folders argument still takes priority |
--check |
Connect to Outlook, print diagnostics + blacklist status to stderr, then exit (no server) |
--version |
Print version and exit |
Everything else is configured by editing the USER CONFIGURATION block at
the top of ms-outlook.py directly (there are no extra CLI flags/env vars
for these):
| Setting | Purpose |
|---|---|
BLACKLIST_TERMS |
Built-in list of classification/compliance terms that cause an item to be withheld from the AI entirely |
BLACKLIST_MATCH_MODE |
"word" (default, whole-term match) or "substring" (for terms containing punctuation) |
RESTRICT_DATE_FORMAT |
Locale-sensitive date format Outlook expects in its Restrict() filter — switch this if outlook_get_calendar returns zero events on a non-US-locale machine |
MAX_BODY_CHARS / CALENDAR_HARD_CAP / SEARCH_SCAN_CAP |
Safety caps on body length / items scanned |
SEARCH_ALL_FOLDERS |
Folder names (matched across every store) that outlook_search_recent searches by default — ["Inbox", "Sent Items", "Archive"]; use outlook_list_folders to see real folder names first. This is only the built-in default: override it at launch with --search-folders, or per call by passing a folders argument to outlook_search_recent |
mcpServers:
- name: outlook
command: C:\path\to\python.exe
args:
- C:\path\to\ms-outlook.py
- --blacklist-file
- C:\config\outlook-blacklist.txt
- --search-folders
- "Inbox,Sent Items,Archive"
env:
PYTHONUTF8: "1"| CLI flag | Purpose |
|---|---|
--check |
Run an offline open/edit/save/reopen self-test and exit (no server) |
--author |
Author name stamped on Word tracked changes (default: the TRACKED_CHANGE_AUTHOR config value in the file). Can also be overridden per-call via the author argument on msword_replace_text |
mcpServers:
- name: msword-py
command: C:\path\to\python.exe
args:
- C:\path\to\ms-word.py
- --author
- Matt
env:
PYTHONUTF8: "1"| CLI flag | Purpose |
|---|---|
--input-dir |
Required. Folder containing the source PDFs |
--output-dir |
Required. Folder to write .md files into |
--recursive |
Also search sub-folders of --input-dir (sub-folder structure is mirrored in the output) |
mcpServers:
- name: pdf2md
command: C:\path\to\python.exe
args:
- C:\path\to\pdf-to-md.py
- --input-dir
- C:\Reference\PDFs
- --output-dir
- C:\Reference\Markdown
- --recursive
env:
PYTHONUTF8: "1"These are natural-language prompts you can give an AI agent (e.g. in Continue's agent mode) once the relevant server is wired in. Each maps to one or more of the tools the server exposes.
- "Search Confluence for our incident response runbook." →
confluence_search - "Find pages in the DOCS space that mention 'release notes' and were updated in the last 30 days." →
confluence_search_cql - "Pull up the full content of Confluence page 393217." →
confluence_get_page - "Open the 'Q3 Roadmap' page in the PROD space and summarise it." →
confluence_get_page_by_title - "List every page under the 'Engineering Handbook' in the DOCS space, direct children only." →
confluence_list_pages_under - "Pull the onboarding runbook into our local knowledge base for offline search." →
confluence_get_page(orconfluence_get_page_by_title), automatically mirrored to Markdown when--kb-dir/CONFLUENCE_KB_DIRis configured, soknowledge-base.py'sreference_search/reference_getcan find it afterwards
- "What reference documents do we have available?" →
reference_list - "Find any reference material about our procurement policy." →
reference_search - "Read the full expense-reporting policy document and tell me the approval limits." →
reference_get - "Search our reference docs for anything about onboarding, then read whichever one covers IT equipment." →
reference_searchfollowed byreference_get
- "Using my knowledge base, can I extend my work trip by 2 days, pay for my own accommodation for the weekend, and fly back Monday?" →
kb_outlineto map the docs, thenkb_read(with asection) on the travel policy's trip-extension section, and reason over it - "What's in my knowledge base?" →
kb_list - "Give me a map of everything in my knowledge base so I can see what topics are covered." →
kb_outline - "Find anything mentioning 'accommodation' or 'per diem'." →
kb_search - "Read just the 'Expense Claims' section of the travel policy." →
kb_readwithsection - "Open the whole onboarding checklist document." →
kb_read(nosection)
- "What Excel workbooks are available for me to look at?" →
excel_list_workbooks - "List the sheets in the 'budget' workbook." →
excel_list_sheets - "What are the column headers on the 'Q3' sheet of the budget workbook?" →
excel_get_headers - "Read rows A1:D50 from the Q3 sheet." →
excel_read_range - "Find every cell in the budget workbook that mentions 'Marketing'." →
excel_search - "Give me the sum, average, min and max of the Revenue column on the Q3 sheet." →
excel_column_stats
- "Show me my 10 most recent unread emails." →
outlook_list_recent_emails - "Search my inbox for anything from 'Jane Smith' about the contract renewal." →
outlook_search_emails - "Open that email from the vendor and summarise the key dates." →
outlook_get_email - "What's on my calendar for the next 7 days?" →
outlook_get_calendar - "What did I send last week?" →
outlook_list_sent_emails - "Find everything about the 'Acme renewal' across my Inbox, Sent Items and Archive from the last month." →
outlook_search_recent - "Search only my 'Projects' and 'Sent Items' folders for anything about the budget review." →
outlook_search_recentwith afoldersargument overriding the default set - "What are my actual Outlook folder names, so I can point the search at the right archive?" →
outlook_list_folders
- "Open the proposal.docx and show me its full text." →
msword_open+msword_get_content - "Find every mention of 'Acme Corp' in the contract and replace it with 'Acme Corporation'." →
msword_search+msword_replace_text - "Add a 'Next Steps' heading and a summary paragraph to the end of the report, then save it." →
msword_add_heading+msword_add_paragraph+msword_save - "Pull out the data from every table in the document as structured rows." →
msword_get_tables - "Add a 3x4 pricing table to the end of the quote document with these values, using the 'Table Grid' style." →
msword_add_table+msword_save - "Change 'DRAFT' to 'FINAL' throughout the report as a tracked change so it shows up as a Word revision for review." →
msword_replace_textwithtrack_changes=true - "What tracked changes are currently in this document, and who made them?" →
msword_list_changes - "Accept all the tracked changes in this document now that legal has signed off." →
msword_accept_all_changes - "Reject all the tracked changes and revert this document to its original wording." →
msword_reject_all_changes
- "Convert every PDF in the reference folder to Markdown." →
convert_all_pdfs - "Convert just the 'procurement policy' PDF to Markdown." →
convert_pdf_to_markdown - "Reconvert all PDFs to Markdown even though some already have a .md file, since the source PDFs changed." →
convert_all_pdfswithforce=true - "Convert all our compliance PDFs (including those in sub-folders) to Markdown so the knowledge-base server can search them." →
convert_all_pdfs(with--recursiveset at startup) feeding intoknowledge-base.py'sreference_search