Give any AI assistant full control over Microsoft Access databases.
Create forms, write VBA, design tables, manage controls, run queries, build relationships, and edit every corner of an .accdb — all through natural language. 61 tools that turn Access into something you can talk to.
No Access expertise required. Just describe what you want.
"Create a form called Invoices with a ListBox, two date filters, and a search button"
"Add a VBA click handler that filters the recordsource by date range"
"Create a table called audit_log with timestamp, user, and action fields"
"List all controls inside the Payment tab and change the combo's row source"
The AI handles the COM automation, design view, VBA modules, binary sections, cache invalidation, and all the ugly parts. You get the result.
- Forms & Reports — create, export, import, screenshot, click, type. Full UI automation loop
- VBA — read, write, replace, compile, and run procedures. Line-level or full-proc editing
- Controls — create, delete, modify, list. Finds controls nested inside TabControl pages
- Tables & SQL — create via DAO, alter, query, batch execute. Linked ODBC tables supported
- Relationships, indexes, references, queries, macros — full CRUD
- Maintenance — compact & repair, decompile bloated databases, export structure docs
Works with Claude Code, Cursor, Windsurf, Continue, or any MCP-compatible client.
- Windows (COM automation is Windows-only)
- Microsoft Access installed (any version that supports VBE, 2010+)
- Python 3.9+
- "Trust access to the VBA project object model" enabled in Access Trust Center
pip install mcp pywin32File → Options → Trust Center → Trust Center Settings → Macro Settings
→ check Trust access to the VBA project object model
Or run the included PowerShell script:
.\enable_vba_trust.ps1Global (available in all projects):
claude mcp add access -- python C:\path\to\access_mcp_server.pyProject-only (creates .mcp.json in current directory):
claude mcp add --scope project access -- python C:\path\to\access_mcp_server.pyAdd to your MCP config file (.mcp.json, mcp.json, or client-specific settings):
{
"mcpServers": {
"access": {
"type": "stdio",
"command": "python",
"args": ["C:\\path\\to\\access_mcp_server.py"]
}
}
}Compatible with any MCP-compliant client (Cursor, Windsurf, Continue, etc.).
| Tool | Description |
|---|---|
access_create_database |
Create a new empty .accdb database file |
access_close |
Close the COM session and release the .accdb file |
| Tool | Description |
|---|---|
access_list_objects |
List objects by type (table, module, form, report, query, macro, all). System tables filtered |
access_get_code |
Export an object's full definition as text |
access_set_code |
Import modified text back (creates or overwrites) |
access_export_structure |
Generate a Markdown index of all modules, forms, reports, queries |
access_delete_object |
Delete a module, form, report, query, or macro. Requires confirm=true |
access_create_form |
Create a new form without triggering the "Save As" MsgBox that blocks COM. Optional has_header for header/footer section |
| Tool | Description |
|---|---|
access_execute_sql |
Run SQL via DAO — SELECT returns rows as JSON (limit default 500). DELETE/DROP/ALTER require confirm_destructive=true |
access_execute_batch |
Execute multiple SQL statements in one call. Supports mixed SELECT/INSERT/UPDATE/DELETE with per-statement results, stop_on_error, and confirm_destructive |
access_table_info |
Show table structure via DAO (fields, types, sizes, required, linked status) |
access_search_queries |
Search text in the SQL of ALL queries at once (find which queries reference a table, field, or keyword) |
access_create_table |
Create a table via DAO with full type, default, description and primary key support in one call. More robust than CREATE TABLE SQL |
access_alter_table |
Modify table structure via DAO: add field, delete field (requires confirm=true), rename field |
| Tool | Description |
|---|---|
access_vbe_get_lines |
Read a line range from a VBA module without exporting the whole file |
access_vbe_get_proc |
Get a procedure's code and position by name |
access_vbe_module_info |
List all procedures with their line numbers |
access_vbe_replace_lines |
Replace/insert/delete lines in a VBA module directly via VBE |
access_vbe_find |
Search text in ONE specific module. To search all modules at once, use access_vbe_search_all |
access_vbe_search_all |
Search text across ALL modules/forms/reports in the database at once |
access_vbe_replace_proc |
Replace a full procedure by name (auto-calculates line bounds). Strips misplaced Option lines, runs structural health check |
access_vbe_patch_proc |
Surgical find/replace within a procedure. Whitespace-tolerant fallback matching + contextual error messages when patches fail |
access_vbe_append |
Append code at the end of a module. Auto-strips Option Explicit/Option Compare to prevent misplacement |
| Tool | Description |
|---|---|
access_list_controls |
List all controls of a form/report with key properties. Controls inside Pages/OptionGroups include a parent field |
access_get_control |
Get the full definition block of a specific control (finds controls inside Pages/OptionGroups) |
access_create_control |
Create a new control via COM in design view. Supports class_name for ActiveX (type 119) ProgID initialization. Use type 128 (acWebBrowser) for native WebBrowser |
access_delete_control |
Delete a control via COM |
access_set_control_props |
Modify control properties via COM in design view |
access_set_multiple_controls |
Modify properties of multiple controls in a single design-view session |
| Tool | Description |
|---|---|
access_export_text |
Export form/report/module as text via SaveAsText. Does NOT open Design view. UTF-16 LE output |
access_import_text |
Import form/report/module from text via LoadFromText. Replaces if exists. Auto-splits CodeBehindForm VBA |
| Tool | Description |
|---|---|
access_get_db_property |
Read a DB property (CurrentDb.Properties) or Access option (GetOption) |
access_set_db_property |
Set a DB property or Access option — creates the property if it doesn't exist |
access_get_form_property |
Read form or report properties (RecordSource, Caption, DefaultView, etc.). object_type required (form or report). Omit property_names for all |
access_set_form_property |
Set form/report properties (RecordSource, Caption, DefaultView, HasModule, etc.) via COM in Design view |
| Tool | Description |
|---|---|
access_list_linked_tables |
List all linked tables with source table, connection string, ODBC flag |
access_relink_table |
Change connection string and refresh link — auto-saves credentials (dbAttachSavePWD) when UID/PWD detected. relink_all=true updates all tables with the same original connection |
| Tool | Description |
|---|---|
access_list_relationships |
List table relationships with field mappings and cascade flags |
access_create_relationship |
Create a relationship between two tables (supports cascade update/delete) |
access_delete_relationship |
Delete a relationship by name |
| Tool | Description |
|---|---|
access_list_references |
List VBA project references with GUID, path, broken/built-in status |
access_manage_reference |
Add (by GUID or file path) or remove a VBA reference — guards against removing built-in refs |
| Tool | Description |
|---|---|
access_compact_repair |
Compact & repair the database — closes, compacts to temp, swaps atomically, reopens |
access_decompile_compact |
Remove orphaned VBA p-code via /decompile, recompile, then compact. Typical reduction: 60-70% on heavily-edited front-end databases. Use when a data-free .accdb exceeds 30-40 MB |
| Tool | Description |
|---|---|
access_manage_query |
Create, modify, delete, rename, or read SQL of a QueryDef. Delete requires confirm=true |
| Tool | Description |
|---|---|
access_list_indexes |
List indexes of a table with fields, primary, unique, foreign flags |
access_manage_index |
Create or delete an index. Create requires fields list with optional sort order |
| Tool | Description |
|---|---|
access_compile_vba |
Compile and save all VBA modules. Optional timeout to auto-dismiss error MsgBox |
| Tool | Description |
|---|---|
access_run_macro |
Execute an Access macro by name |
access_run_vba |
Execute a VBA Sub/Function. Standard modules via Application.Run, form modules via Forms.FormName.Method syntax (COM). Optional timeout auto-dismisses MsgBox/InputBox |
access_eval_vba |
Evaluate a VBA expression via Application.Eval. Domain functions, VBA built-ins, open form properties, standard module functions. Auto-fallback via temp module for class instances and other expressions Eval cannot resolve |
| Tool | Description |
|---|---|
access_output_report |
Export a report to PDF, XLSX, RTF, or TXT via DoCmd.OutputTo |
| Tool | Description |
|---|---|
access_transfer_data |
Import/export data between Access and Excel (.xlsx) or CSV. Supports range (Excel) and spec_name (CSV) |
| Tool | Description |
|---|---|
access_get_field_properties |
Read all properties of a table field (DefaultValue, ValidationRule, Description, Format, etc.) |
access_set_field_property |
Set a field property — creates the property if it doesn't exist |
| Tool | Description |
|---|---|
access_list_startup_options |
List 14 common startup options (AppTitle, StartupForm, AllowBypassKey, etc.) with current values |
| Tool | Description |
|---|---|
access_screenshot |
Capture the Access window as PNG. Optionally opens a form/report first. Returns path, dimensions (original + image), and metadata. Configurable max_width (default 1920), wait_ms (pumps Windows messages — Timer events fire, ActiveX initializes), and open_timeout_sec (default 30 — sends ESC to cancel if Form_Load hangs on a slow query) |
access_ui_click |
Click at image coordinates on the Access window. Coordinates are relative to a previous screenshot (image_width required for scaling). Supports left, double, and right click |
access_ui_type |
Type text or send keyboard shortcuts. text for normal characters (WM_CHAR), key for special keys (enter, tab, escape, f1-f12, arrows, etc.), modifiers for combos (ctrl, shift, alt) |
| Tool | Description |
|---|---|
access_find_usages |
Search a name across VBA code, query SQL, and control properties (ControlSource, RecordSource, RowSource, SourceObject, DefaultValue, ValidationRule, LinkChildFields, LinkMasterFields) in one call |
| Tool | Description |
|---|---|
access_tips |
On-demand tips and gotchas. Topics: eval, controls, gotchas, sql, vbe, compile, design. Zero tokens until called |
1. access_list_objects → find the module or form name
2. access_vbe_module_info → get procedure list and line numbers
3. access_vbe_get_proc → read the specific procedure
4. access_vbe_replace_lines → apply targeted line-level changes
5. access_close → release the file when done
1. access_get_code → export to text
2. (edit the text)
3. access_set_code → reimport — binary sections are restored automatically
1. access_create_form(db, "myForm", has_header=true) → creates empty form
2. access_create_control(db, "form", "myForm", "CommandButton", {Name: "btn1", ...})
3. access_vbe_append(db, "form", "myForm", code) → add VBA event handlers
4. access_set_form_property(db, "form", "myForm", {HasModule: true, OnCurrent: "[Event Procedure]"})
1. access_screenshot(db, "form", "myForm") → capture form as PNG
2. (LLM reads the image and identifies UI elements)
3. access_ui_click(db, x=850, y=120, image_width=1920) → click a button
4. access_ui_type(db, text="search term") → type in a field
5. access_ui_type(db, key="enter") → press Enter
6. access_screenshot(db) → verify the result
- Access runs visible (
Visible = True) so VBE COM access works correctly. - One Access instance is shared across all tool calls (singleton session). Opening a different
.accdbcloses the previous one. - COM thread isolation: All COM calls run in a dedicated single-thread executor (
_com_executor) withCoInitialize(). This keeps COM in one STA thread while the asyncio event loop stays free for stdio I/O, preventing-32602errors from message corruption. - Auto-reconnect: if the COM session becomes stale (Access crashed, closed manually, or COM corruption), the server detects it via a health check and reconnects automatically on the next tool call.
access_get_codestrips binary sections (PrtMip,PrtDevMode, etc.) from form/report exports —access_set_coderestores them automatically before importing.- All VBE line numbers are 1-based.
- ActiveX controls (type 119 =
acCustomControl):access_create_controlnow accepts aclass_nameparameter with the ProgID (e.g.Shell.Explorer.2) to initialize the OLE control. For WebBrowser specifically, use type 128 (acWebBrowser) which creates a native control without OLE complexity. Settingctrl.Classfrom COM may not work for all ActiveX controls — manual insertion from the ribbon remains the most reliable method. access_run_vba: Now supports form module procedures viaForms.FormName.Methodsyntax (direct COM access, form must be open). Also supportstimeoutparameter — if exceeded, auto-dismisses MsgBox/InputBox dialogs. For more flexible form interaction, useaccess_eval_vba.- Timer events (
Form_Timer): Now fire duringaccess_screenshotwhenwait_ms > 0— the wait loop pumps Windows messages viapythoncom.PumpWaitingMessages(). Other tools still block the message pump. access_vbe_appendpreviously HTML-encoded&as&due to MCP transport escaping. Fixed in v0.7.3 with explicithtml.unescape()decoding.
The MCP Python SDK (v1.26.0) has a catch-all except Exception in mcp/shared/session.py that swallows real errors and returns a generic -32602 code with no detail. A local patch is applied to this machine that includes the actual exception and traceback in the error response. If you upgrade the mcp package, re-apply the patch — see CLAUDE.md for details.
Bug fixes — thanks to @CaptainStormfield and @unmateria (wizard-during-compact report), and @TvanStiphout-Home (class module request):
access_decompile_compactsilently launched Report Wizard on every call (root cause of the "wizard hang" report):maintenance.pyhadapp2.RunCommand(137) # acCmdCompileAllModules = 137— but 137 isacCmdNewObjectReport, notacCmdCompileAllModules(the correct value is 125 for compile-only or 126 for compile-and-save). Everyac_decompile_compactinvocation was silently opening the Report Wizard and blocking the COM thread indefinitely until a human clicked Cancel. The "intermittent" symptoms in the original report were actually 100% reproducible — the wizard was always there, just sometimes hidden behind other windows. Fix: changedRunCommand(137)→RunCommand(126)(acCmdCompileAndSaveAllModules).access_compact_repair/access_decompile_compacthang on unexpected dialogs (defence-in-depth for any future wizard, ODBC credential prompts, recovery dialogs): NeitherCompactRepairnor the/decompilesubprocess had dialog protection — onlyOpenCurrentDatabasedid. Fix: new_call_with_dialog_watchdog(app, label, callable_fn)generic helper wraps any blocking COM call with a polling daemon thread that dismisses any Access-owned dialog every 0.5s via_dismiss_access_dialogs._compact_with_watchdogis a thin wrapper around it. TheRunCommand(126)call inac_decompile_compactis also wrapped in this helper._Session._decompile()andac_decompile_compactreplace their fixedtime.sleep(3) + sleep(5)sequence with a polling loop that calls_dismiss_dialogs_by_pid(proc.pid)on the standalone MSACCESS subprocess.- OpenCurrentDatabase watchdog no longer sends
VK_RETURN(v0.7.19 regression): The old one-shot watchdog sent Enter (VK_RETURN) to dismiss blocking dialogs — dangerous on wizards, as Enter clicks "Next >" and advances the wizard, creating strayReport1/Form1objects. Rewritten as a polling loop that delegates to_dismiss_access_dialogswith the new Cancel-first button priority. _try_click_button()button priority fix: Previously used asetof target button labels with undefined iteration order, so it could click any of End/OK/Cancel depending onsethash ordering. Now uses an explicit priority tuple("cancel", "cancelar", "end", "finalizar", "ok", "aceptar")— Cancel first so wizards cancel cleanly, End second to preserve existingac_run_vbabehaviour on VBA runtime-error dialogs (which have no Cancel button).- Wizard title detection:
_dismiss_dialogs_by_pidnow matches windows whereclass == '#32770'OR the title contains"wizard"/"asistente"(case-insensitive). Catches non-standard wizard windows that don't use the#32770class.
New feature:
access_set_code(object_type="class_module", ...): Creates a VBA class module by injecting the fourAttribute VB_*lines at the top of the text. Previously,object_type="module"always created a standard module. Tested on production (Access 2016):Application.LoadFromText(acModule=5)distinguishes class from standard modules by the presence ofAttribute VB_GlobalNameSpace,Attribute VB_Creatable,Attribute VB_PredeclaredId,Attribute VB_Exposedat the top of the file — NOT by aVERSION 1.0 CLASSheader (that header is forVBComponent.Export/Import, a different mechanism; passing it toLoadFromTextmakes Access interpret the header lines as literal VBA code and creates a corrupt Type=1 standard module). New_ensure_class_module_header(code, name)strips any BOM, strips any legacyVERSION 1.0 CLASS/BEGIN/END/Attribute VB_Nameblock the user may have pasted from aVBComponent.Exportfile, detects existingAttribute VB_GlobalNameSpace(round-trip safe — feedingaccess_get_codeoutput back does not duplicate), and prepends the 4 attribute lines with CRLF endings.class_modulere-usesacModule=5under the hood — no changes needed inaccess_get_codeoraccess_delete_object. Verified on production DB round-trip: create → read → re-import → overwrite → delete, all withVBComponent.Type == 2.
Bug fixes — thanks to @CaptainStormfield (PR #17):
- Property Get/Let/Set procedures invisible to VBE tools: All VBE call sites (
get_proc,module_info,replace_proc,patch_proc,find) hardcodedkind=0(vbext_pk_Proc). Property procedures requirekind=3(vbext_pk_Property). New helpers_proc_kind(),_proc_bounds(),_proc_of_line()try kind=0 first and fall back to kind=3 - Wrong VBProject after decompile+compact:
app.VBE.VBProjects(1)could returnacwzmain(wizard library) instead of the user's database project. New_get_vb_project(app)enumerates all VBProjects and matches by.FileNameagainst the open database path - "Subscript out of range" after decompile:
VBComponents(name)could fail even though the component exists._get_code_module()now retries once after forcing VBE initialisation (opens form/report briefly in Design view, or togglesVBE.MainWindow.Visiblefor modules)
Bug fixes — thanks to @CaptainStormfield (PR #11):
access_get_form_propertycrash on binary GUID properties:_serialize_valuehandledbytesbut notmemoryview, causing JSON serialization crash. Fix: extended type check to(bytes, memoryview)access_find_usagesmissed subform link references:LinkChildFieldsandLinkMasterFieldswere not inCONTROL_SEARCH_PROPS, so stale subform link references after table/field renames went undetectedaccess_list_controls/access_get_controlmissed Subform controls: Access exportsBegin Subform(lowercase f) but the constant was"SubForm"(capital F), causing case-sensitive matching to skip subforms entirely. Also fixed wrong control number in tips (was 114, correct is 112)- Spurious duplicate-label warnings in VBE health check: The check scanned modules flat, flagging
ErrHandler:as duplicate when it appeared in separate procedures. VBA labels are procedure-scoped — fix tracks Sub/Function/Property boundaries and only flags duplicates within the same procedure access_vbe_append/access_vbe_replace_lines"Catastrophic failure" after design operations (#12 — thanks @CaptainStormfield): These VBE tools did not close the form/report in Design view before accessing the VBE CodeModule, causingcom_error(-2147418113). Fix: all VBE write functions now close the object and invalidate_cm_cachebefore accessing VBE, matching the pattern already used byaccess_vbe_replace_procandaccess_vbe_patch_proc
OpenCurrentDatabase watchdog — auto-dismiss blocking dialogs:
OpenCurrentDatabasenow runs in a background thread with a 10-second watchdog. If the open blocks (recovery dialog, save changes prompt, or any unexpected modal dialog), the watchdog:- Captures a screenshot of the Access window and saves to
%TEMP%\access_blocked_*.png - Detects the blocking dialog via
GetForegroundWindow - Sends Enter (
VK_RETURN) viaPostMessageWto dismiss it (accepts default button) - Logs the screenshot path for debugging
- Captures a screenshot of the Access window and saves to
- Recovery dialog suppression: Before every
OpenCurrentDatabase, writesDisableAllCallersWarning=1andDoNotShowUI=1toHKCU\Software\Microsoft\Office\16.0\Access\Resiliencyregistry key. This prevents the "last time you opened this file it caused a serious error" dialog that appears after a crash orStop-Process
access_compile_vba — complete rewrite for reliable error detection:
- VBE CommandBars compile: Now compiles via
VBE.CommandBars("Menu Bar").Controls("Debug").Execute()instead ofRunCommand(126). This is equivalent to clicking Debug > Compile in VBE and reliably compiles ALL modules including form/report (RunCommand silently skipped them) - Error dialog text capture: Watchdog reads the Microsoft error dialog text via Win32
GetWindowTextbefore dismissing it. Returns the exact error message (e.g. "Compile error: Block If without End If") + module name + line number viaVBE.ActiveCodePane.GetSelection() - Watchdog always active: Polls every 0.5s regardless of timeout parameter, preventing hangs from unexpected dialogs
- Block mismatch parser (fallback): When
IsCompiled=Falsebut no dialog was caught,_find_block_mismatches()statically analyzes VBA code for unclosed blocks. Handles single-line If, comments after Then,#If/#End If, and colon-separated statements - Lint removed from compile:
_lint_form_modules()no longer runs during compilation — it opened every form in Design view causing dozens of "Save changes?" dialogs and broken reference errors
Auto-decompile moved to compile only:
/decompile+ SHIFT now runs automatically on first compile per session (not on every DB open). Previous approach caused SHIFT key stuck issues and MSACCESS.EXE process accumulation on MCP reconnect
VBE robustness — 3 layers of protection for VBA editing:
- Whitespace-tolerant matching in
access_vbe_patch_proc: When exact match fails, a whitespace-normalized fallback strips leading indentation and retries. If both fail,difflib.SequenceMatcherfinds the closest line and returns contextual error with 3 surrounding lines — no more opaque "not found" errors - Structural health check on all write operations: After every VBE write (
replace_lines,replace_proc,patch_proc,append), checks for: (1)Option Explicit/Option Comparemisplaced below line 5, (2) duplicate labels, (3) unexpected line count changes (batch mode). Warnings in return string, never fails the operation - Option Explicit/Compare protection:
access_vbe_appendauto-stripsOptionlines (they'd end up at the bottom of the module).replace_procandpatch_procstrip them when replacing non-top procedures.access_set_codeandaccess_import_textauto-prependOption Compare Database+Option Explicitwhen missing from injected VBA
Bug fix:
access_find_usagesmissed SubForm SourceObject: Control property search didn't includeSourceObject, so SubForm/SubReport references to other forms were invisible. Deleting a form that was a SubForm's SourceObject would break the parent form silently. Fix: addedSourceObjecttoCONTROL_SEARCH_PROPS
Improvements:
access_get_codedescription now recommendsaccess_vbe_get_procfor reading specific VBA procedures (faster, smaller output — avoids 90KB+ exports for large forms)access_tips("vbe")expanded with guidance: useaccess_vbe_module_info→access_vbe_get_procflow instead ofaccess_get_codefor VBA investigationaccess_tips("gotchas")documents that SHIFT bypass on database open is automatic (no manual intervention needed)
Usability improvements (reduce LLM hallucination of parameter names):
access_vbe_append: renamednew_codeparameter tocodefor consistency withaccess_set_codeand other toolsaccess_vbe_find: description now clarifies it searches ONE module and suggestsaccess_vbe_search_allfor cross-module searchaccess_get_form_property: description now explicitly statesobject_typeis required (formorreport)
Improvement:
access_eval_vbaauto-fallback:Application.Evalcannot resolve class default instances (VB_PredeclaredId), variables, or project-level symbols. Now when Eval fails, the tool automatically creates a temp standard module with a wrapper function, calls it viaApplication.Run, and cleans up. If both fail, the error includes both messages and suggestsaccess_run_vba. Tool description updated to clarify supported/unsupported patterns
Bug fix:
access_compile_vbafalse positive — reported "compiled" on broken code: Two root causes: (1) VBE edits via COM don't always invalidateIsCompiled, soRunCommand(126)on an already-compiled project was a no-op. (2) Without the VBE window visible,RunCommand(126)silently skips form/report module compilation. Fix: dirty trick (insert+remove dummy comment) forcesIsCompiled=False, then VBE MainWindow is opened before compiling soRunCommandbehaves like clicking Debug > Compile. Additionally, new_verify_module_structure()scans all modules (standard + form/report) for executable code outside Sub/Function blocks as a safety net — catches the specific pattern of accidentally deleted Sub headers leaving orphan code
Bug fix:
- Compact/decompile reopen could trigger AutoExec/startup forms/wizards:
ac_compact_repairandac_decompile_compactreopened the database after compacting via directapp.OpenCurrentDatabase(), bypassing the SHIFT key handling in_switch(). If the database had AutoExec macros or startup forms (e.g. Report Wizard), the reopen would block COM indefinitely. Fix: new_Session.reopen()method forces_switch()(SHIFT held + auto-close forms) for all reopens in maintenance operations
Bug fixes:
- AutoExec / startup forms block
OpenCurrentDatabaseindefinitely: Databases withAutoExecmacros that open modal forms (e.g. Northwind Developer Edition's welcome/login dialog viaacDialog) block the COM call until the user manually closes the form. Fix:_switch()now holds the Shift key viawin32api.keybd_event(VK_SHIFT)duringOpenCurrentDatabase— the standard Access bypass for AutoExec and startup forms. After opening, any auto-opened forms are closed as a safety net.AutomationSecurity = 3does NOT work (Access ignores it for database-level AutoExec macros). VK_ESCAPE is unreliable (doesn't reach modal forms) - MCP clients sending integer/boolean arguments as strings: Some MCP clients (e.g. Claude Desktop) serialize ALL tool arguments as strings. The MCP SDK validates against the JSON Schema before
call_tool()runs, sostart_line: "1"fails with'1' is not of type 'integer'. Fix:_fixup_schema()runs at module load and widens all 58 tool schemas to accept["integer", "string"]and["boolean", "string"]._coerce_arguments()incall_tool()converts string args to the expected type before dispatch
Bug fix:
access_list_controls/access_get_controldidn't find controls inside Pages or OptionGroups: The text parser (_parse_controls) consumed the entireBegin Page ... Endblock as one control and skipped all children inside it. Fix: new_CONTAINER_TYPESset ({"Page", "OptionGroup"}) and acontainer_stackmechanism — when the parser finds a container type, it records the container and re-scans inside the block instead of jumping past it. Child controls now include a"parent"field with the container's name.delete_controlandset_control_propswere unaffected (they use COM directly)
Bug fix:
- Intermittent
-32602 Invalid request parameterserrors: Root cause — thecall_toolhandler wasasyncbut ran blocking COM calls synchronously, stalling the asyncio event loop. When the event loop couldn't read stdin fast enough, the MCP SDK's message parser received truncated or merged JSON-RPC frames, causing Pydanticmodel_validateto fail with a catch-all-32602error. Fix: all COM work now runs in a dedicated single-threadThreadPoolExecutor(_com_executor) withCoInitialize(), keeping the event loop free for stdio I/O. Thecall_toolasync wrapper usesloop.run_in_executor()to delegate to the COM thread
Bug fix:
access_screenshotOpenForm hang:DoCmd.OpenFormcould block indefinitely when a form'sForm_Loadevent ran a slow or blockedOpenRecordset(ODBC query to SQL Server). Newopen_timeout_secparameter (default 30 s) starts a daemon thread before opening the form. IfOpenFormdoes not return within the timeout, the thread sendsPostMessage(WM_KEYDOWN/WM_KEYUP, VK_ESCAPE)to the Access window to cancel the pending DAO operation, then raisesTimeoutErrorwith a descriptive message. The hwnd is captured beforeOpenFormblocks so the cancel thread does not need COM access (STA restriction).open_timeout_seccan be increased for forms that are legitimately slow to load
New tool:
access_decompile_compact— removes orphaned VBA p-code by launchingMSACCESS.EXE /decompile, recompiling all modules, then running Compact & Repair. Compact alone cannot reclaim p-code space; this combination achieves 60-70% size reduction on heavily-edited front-end databases (tested: 69 MB → 26 MB). Launches Access as a subprocess, waits 8 s for decompile to complete, kills the process, reconnects via COM for recompile, then compact
New tool:
access_create_form— create a new form safely via COM without the "Save As" MsgBox that blocks the session. UsesCreateForm()→DoCmd.Save(acForm, autoName)→DoCmd.Close(acForm, autoName, acSaveNo)→DoCmd.Rename(desired, acForm, autoName). Optionalhas_header=trueto create with header/footer section viaRunCommand(36)
Known limitations reduced:
- Timer events fixed:
access_screenshotnow usespythoncom.PumpWaitingMessages()loop duringwait_msinstead oftime.sleep().Form_Timerevents fire, ActiveX controls initialize, WebBrowser navigates - MsgBox/InputBox timeout:
access_run_vbaandaccess_compile_vbanow accept optionaltimeout(seconds). If exceeded,_dismiss_access_dialogs()finds Access modal dialogs (class#32770) viawin32gui.EnumWindowsand sendsWM_CLOSEto dismiss them - Form module support: New
access_eval_vbatool — evaluates expressions viaApplication.Eval(form properties, form module functions, domain functions, VBA built-ins).access_run_vbanow supportsForms.FormName.Methodsyntax for direct COM access to open forms - ActiveX
class_name:access_create_controlnow acceptsclass_nameparameter for ActiveX (type 119) — setsctrl.Classwith ProgID to initialize OLE. Type 128 (acWebBrowser) documented as native WebBrowser alternative. New AcControlType constants added (128-134)
Improvements:
access_compile_vbatimeout + error diagnostics: Optionaltimeoutto auto-dismiss MsgBox on compilation error. Reports exact module, line, code context viaVBE.ActiveCodePane, and captures dialog screenshotaccess_tips(new tool): On-demand knowledge base with tips and gotchas (eval, controls, sql, vbe, compile, design, gotchas). Zero tokens until calledaccess_list_controls/access_get_control: Now detect conditional formatting — showformat_conditionscount when a control hasConditionalFormatentries
Bug fix:
- "You already have the database open" after MCP reconnect:
_switch()now catches this error and syncs internal state instead of failing. Happens when the MCP server restarts but Access.exe keeps running with the DB open from the previous session
Bug fix:
access_run_vbawas completely broken — every call failed withDISP_E_BADPARAMCOUNT(-2147352562). Root cause: pywin32's late-boundDispatchusesIDispatch.Invoke()which only passes provided arguments. Access'sApplication.Runhas 31 parameters (1 required + 30 optional) and its COM server rejects calls missingVT_ERROR/DISP_E_PARAMNOTFOUNDmarkers for the 30 optional params. Fix: new_invoke_app_run()helper calls_oleobj_.InvokeTypes()directly with full argument types andpythoncom.Missingpadding — the same COM protocol that early-bound (EnsureDispatch) wrappers generate, but without changing the binding model for the other 53 tools
Reliability improvements:
- Auto-reconnect COM:
_Session.connect()now performs a health check (app.Visible) before every tool call. If the COM session is stale (Access crashed, closed manually, or corrupted), it automatically reconnects instead of failing with cryptic COM errors access_vbe_append/access_vbe_replace_lines: fixed HTML entity encoding bug where&was silently converted to&by MCP transport. Now applieshtml.unescape()to decode entities before inserting code- VBE cache invalidation:
_get_code_modulenow evicts stale cache entries on failure, preventing cascading "Subscript out of range" errors afteraccess_set_codeor COM reconnection - Tool descriptions updated with known limitations:
access_run_vba: documents that only standard module procedures work (not form/report modules) and that MsgBox/InputBox blocks indefinitelyaccess_create_control: documents that ActiveX (type 126) creates empty containers without OLE initializationaccess_screenshot: documents that Timer events do not fire during capture (no message pump)
Robustness improvements:
access_relink_table: added rollback — ifTransferDatabasefails after deleting the old link, the original link is restored automatically. Previously the table would be left deleted with no replacementaccess_execute_sql/access_execute_batch: fixed silent retry swallowing errors. ThedbSeeChangesretry pattern now preserves the original error message when both attempts fail, instead of showing only the retry erroraccess_set_code: backup before import now includes modules (previously only forms/reports). If a module import fails, the original is restored viaLoadFromTextaccess_run_vba: tool description now warns thatMsgBox/InputBoxin VBA will block the call indefinitely. Recommends usingaccess_ui_click/access_ui_typefor UI interaction
Bug fix:
- Fixed
access_relink_tablenot persisting ODBC credentials:_DB_ATTACH_SAVE_PWDconstant was 65536 (wrong — that'sdbAttachExclusive) instead of 131072 (dbAttachSavePWD). Tables relinked with UID/PWD would lose credentials on next database open, causing login prompts - Replaced DAO
CreateTableDef+Attributesapproach withDoCmd.TransferDatabase(acLink, ..., StoreLogin=True)which works reliably from Python COM (settingAttributesbeforeAppendworks in native VBA but fails via pywin32 with Type Mismatch)
New tools (3):
access_screenshot— capture the Access window as PNG usingPrintWindowAPI with DPI awareness. Optionally opens a form/report, captures, then closes it. Resizes to configurablemax_widthfor token efficiencyaccess_ui_click— click at image coordinates on the Access window. Scales from screenshot space to screen space automatically. Supports left, double, and right clickaccess_ui_type— type text viaWM_CHARor send keyboard shortcuts viakeybd_event. Supports special keys (enter, tab, escape, F1-F12, arrows) and modifier combos (ctrl, shift, alt)
Infrastructure:
- DPI awareness (
SetProcessDpiAwareness(2)) set at module load for accurate window dimensions - COM
hWndAccessApphandled for both property and method variants
New tools (3):
access_execute_batch— execute multiple SQL statements in a single call with per-statement results,stop_on_errorflag, and batch destructive guardaccess_get_form_property— read form/report properties (RecordSource, Caption, DefaultView, HasModule, etc.) via COM in design viewaccess_set_multiple_controls— modify properties on multiple controls in a single design-view open/close cycle
New tools (5):
access_create_database— create a new empty.accdbdatabase viaNewCurrentDatabaseaccess_delete_object— delete modules, forms, reports, queries, or macros viaDoCmd.DeleteObject(requiresconfirm=true)access_run_vba— execute VBA Sub/Function viaApplication.Runwith optional arguments and return value captureaccess_delete_relationship— delete a table relationship by name via DAOaccess_find_usages— cross-reference search across VBA code, query SQL, and control properties in a single call
Enhancements:
access_list_objectsnow supportsobject_type="table"viaAllTables(system/temp tables filtered)
New tools (10):
access_manage_query— create, modify, delete, rename, or read SQL of QueryDefs via DAOaccess_list_indexes/access_manage_index— list table indexes; create or delete indexes with field order and primary/unique flagsaccess_compile_vba— compile and save all VBA modules (acCmdCompileAndSaveAllModules)access_run_macro— execute an Access macro by nameaccess_output_report— export reports to PDF, XLSX, RTF, or TXT via DoCmd.OutputToaccess_transfer_data— import/export data between Access and Excel (.xlsx) or CSV via DoCmd.TransferSpreadsheet/TransferTextaccess_get_field_properties/access_set_field_property— read all field properties; set or create field-level properties (DefaultValue, ValidationRule, Description, etc.)access_list_startup_options— list 14 common startup options with current values
New tools (9):
access_get_db_property/access_set_db_property— read/write database properties (AppTitle, StartupForm, etc.) and Access application optionsaccess_list_linked_tables/access_relink_table— list linked tables with connection info; change connection strings with bulk relink supportaccess_list_relationships/access_create_relationship— list and create table relationships with cascade flagsaccess_list_references/access_manage_reference— list VBA references (with broken/built-in detection); add by GUID or path, remove by nameaccess_compact_repair— compact & repair with atomic file swap and automatic reopen
New tools:
access_search_queries— search text in the SQL of all queries at once (equivalent to iteratingQueryDefswithInStr)
Improvements:
access_execute_sql: addedlimitparameter (default 500, max 10000) to cap SELECT results and prevent token explosionsaccess_execute_sql: addedconfirm_destructiveflag — DELETE/DROP/TRUNCATE/ALTER now require explicit confirmationaccess_vbe_search_allandaccess_search_queries: addedmax_resultsparameter (default 100) withtruncatedindicatoraccess_export_structure: now returns the Markdown content directly (no extra Read needed)- All tool descriptions compacted ~60% to reduce token overhead per MCP session
New tools:
access_vbe_search_all— search text across all modules, forms, and reports in a single callaccess_table_info— inspect table structure via DAO (field names, types, sizes, required flags, record count, linked status)access_vbe_replace_proc— replace or delete a full procedure by name without manual line arithmeticaccess_vbe_append— append code to the end of a module safely
Bug fixes:
- Fixed
access_set_codecorrupting VBA modules by writing UTF-16 BOM; modules now usecp1252(ANSI) encoding as Access expects - Fixed
access_list_controlsreturning empty results; control parser rewritten to correctly findBegin <TypeName>blocks at any nesting depth - Fixed
access_vbe_replace_procfailing with catastrophic COM error after design-view operations; now closes the form in Design view and invalidates cache before accessing VBE - Fixed
access_vbe_module_inforeporting inconsistentstart_line/countvalues; now uses COMProcStartLineconsistently and clamps count to module bounds - Added boundary validation to
access_vbe_replace_lines— checksstart_linerange and clampscountto prevent overflows
Improvements:
- All design-view operations (
access_create_control,access_delete_control,access_set_control_props) now invalidate all internal caches in theirfinallyblock