Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
searchA

Search one domain's Active Directory catalogue: every class and attribute in its schema, which attributes are confidential, each attribute's syntax and whether it holds one value, which attributes a class may hold, the LDAP controls the domain controller supports, and the extended rights. Use it before execute to find the right attribute or class rather than guessing.

The catalogue is read from the domain the first time you search it, which takes a few seconds, and kept for the session. Pass refresh: true after a schema change. Your script runs with no network and cannot reach the domain.

Write the body of an async function and "return" the value you want back. Output is capped at about 10,000 tokens, so filter inside the script.

Available in the script: declare const catalogue: { domain: string; readAt: string; dc: string; schemaNamingContext: string; forestFunctionality: number; domainFunctionality: number; // 10 = Windows Server 2025 attributes: Record<string, { // keyed by lDAPDisplayName, e.g. "member" oid: string; guid?: string; syntax: string; // e.g. "DN", "UnicodeString", "LargeInteger", "SID" single: boolean; confidential?: true; indexed?: true; systemOnly?: true; linkID?: number; range?: [number | null, number | null]; propertySet?: string; description?: string; }>; classes: Record<string, { // keyed by lDAPDisplayName, e.g. "user" oid: string; guid?: string; kind: "structural" | "abstract" | "auxiliary" | "88"; parent: string; must: string[]; may: string[]; // including inherited and auxiliary-class attributes auxiliary: string[]; possibleSuperiors: string[]; description?: string; }>; controls: Array<{ oid: string; name?: string }>; // what the DC supports extendedRights: Record<string, { // keyed by name, e.g. "User-Force-Change-Password" displayName: string; guid: string; kind: "control" | "propertySet" | "validatedWrite" | "other"; appliesTo: string[]; }>; // Policy settings from the ADMX files (the domain's central store, or the local PolicyDefinitions). // To set one with gpo.set, prefix key with HKLM\ for class Machine or HKCU\ for class User. // A policy's own valueName is set to 1 to enable it; elements are its extra values. policies: Array<{ name: string; displayName: string; class: "Machine" | "User" | "Both"; key: string; valueName?: string; category: string; file: string; elements: Array<{ type: string; id?: string; valueName?: string; key?: string }> }>; policiesSource?: string; policiesError?: string; };

Examples: // Confidential attributes return Object.entries(catalogue.attributes).filter(([, a]) => a.confidential).map(([n]) => n);

// Attributes about passwords, with their syntax return Object.entries(catalogue.attributes).filter(([n]) => /pwd|password/i.test(n)).map(([n, a]) => ({ n, syntax: a.syntax, single: a.single }));

// Everything a user object may hold return catalogue.classes.user.may.length;

executeA

Run a JavaScript script against one Active Directory domain. Use this for any read, filter, join, count, or change. Write the body of an async function and "return" the value you want back. Only what you return (and console.log) comes back to you, so filter and pick attributes inside the script. Output is capped at about 10,000 tokens.

Every call runs as the Windows user the server runs as, over Kerberos with signing and sealing, against the domain's PDC emulator, so Active Directory's own permissions decide what succeeds. Use connections_list to find domain aliases. Name objects by DN. Pass your own attributes; without them you get name, objectClass and sAMAccountName. A connection added in read mode refuses add, modify, delete, move, addAce and removeAce.

Available in the script: declare const ad: { // One object by DN, or null if there is none. Default attributes: name, objectClass, sAMAccountName. ["*"] for all. get(dn: string, attributes?: string[], opts?: { controls?: Controls }): Promise<Entry | null>; // Objects matching an LDAP filter. base defaults to the domain head, scope to "sub", max to 1000 (cap 20000). // more is true when there were more than max. Multi-valued attributes like member come back whole. search(opts: { filter?: string; base?: string; scope?: "base" | "one" | "sub"; attributes?: string[]; max?: number; controls?: Controls }): Promise<{ entries: Entry[]; more: boolean }>; // The account the server runs as, and the PDC emulator it talks to. whoami(): Promise<{ user: string; pdc: string; defaultNamingContext: string }>; // Writes. A read-mode connection refuses these four before anything is sent. add(dn: string, attributes: Record<string, Value | Value[]>): Promise<{ dn: string }>; // include objectClass modify(dn: string, changes: Array<{ op: "add" | "replace" | "delete"; attribute: string; values?: Value | Value[] }>, opts?: { controls?: Controls }): Promise<{ dn: string }>; delete(dn: string, opts?: { tree?: boolean }): Promise<{ dn: string }>; move(dn: string, to: { newParent?: string; newName?: string }): Promise<{ dn: string }>; // newName is an RDN, e.g. "CN=New Name" // Permissions. getAcl reads the owner and DACL, which needs no admin rights. A read-mode connection refuses addAce and removeAce. getAcl(dn: string): Promise<{ dn: string; owner: Principal; protected: boolean; aces: Ace[] }>; // objectType: an attribute, class or extended right name (find it with search) or a GUID. inheritedObjectType: a class. addAce(dn: string, ace: NewAce): Promise<{ dn: string }>; // Removes the entry that matches exactly; an Ace from getAcl can be passed back as it is. removed is false if none matched. removeAce(dn: string, ace: NewAce | Ace): Promise<{ dn: string; removed: boolean }>; }; // Group Policy, through Microsoft's GroupPolicy module on the PDC emulator. g is a GPO's name or id. // A read-mode connection refuses create, delete, link, unlink, set and remove. declare const gpo: { list(): Promise<Gpo[]>; // With where it is linked and every registry policy value it sets. get(g: string): Promise<Gpo & { links: Link[]; computerSettings: Setting[]; userSettings: Setting[]; securitySettings: SecuritySettings }>; create(name: string, opts?: { comment?: string }): Promise; delete(g: string): Promise<{ id: string; name: string; deleted: true }>; // Creates the link, or changes it if the GPO is already linked there. target is an OU or the domain DN. link(g: string, target: string, opts?: { enabled?: boolean; enforced?: boolean; order?: number }): Promise<{ id: string; links: Link[] }>; unlink(g: string, target: string): Promise<{ id: string; links: Link[] }>; // key starts with HKLM\ (computer) or HKCU\ (user). Find keys with search over catalogue.policies. set(g: string, s: { key: string; valueName: string; type: "String" | "ExpandString" | "DWord" | "QWord" | "MultiString"; value: string | number | string[] }): Promise; remove(g: string, key: string, valueName?: string): Promise; // Backup-GPO to a folder on the machine adslayer runs on. Allowed on a read connection. backup(g: string, path: string): Promise<{ id: string; backupId: string; path: string; timestamp: string }>; }; interface Gpo { id: string; name: string; status: string; created: string; modified: string; computerVersion: number; userVersion: number; wmiFilter: string | null } interface Link { target: string; enabled: boolean; enforced: boolean; order: number } interface Setting { key: string; valueName: string; type: string; value: unknown } // From the GPO's security template (GptTmpl.inf). Read-only for now. Empty when the GPO sets none. interface SecuritySettings { systemAccess: Record<string, number | string>; // password and lockout policy, e.g. MinimumPasswordLength, LockoutBadCount eventAudit: Record<string, number | string>; privilegeRights: Record<string, GpoPrincipal[]>; // user rights, e.g. SeInteractiveLogonRight groupMembership: Array<{ group: GpoPrincipal; members?: GpoPrincipal[]; memberOf?: GpoPrincipal[] }>; // Restricted Groups registryValues: Record<string, { type: number; value: number | string }>; // Security Options, keyed MACHINE... other: Record<string, Record<string, string>>; } interface GpoPrincipal { sid: string | null; name: string | null } // Every attribute is an array. GUIDs and SIDs are strings; other binary values are { base64 }. interface Entry { dn: string; attributes: Record<string, Array<string | { base64: string }>> } type Value = string | number | boolean | { base64: string }; interface Principal { sid: string; name: string | null } // name e.g. "CONTOSO\Helpdesk" // rights: e.g. "GenericAll", "ReadProperty", "WriteProperty", "ExtendedRight", "CreateChild", "DeleteChild", "Delete", "DeleteTree", "WriteDacl" // inheritance: "All" = this object and everything below it; "Descendents" = only below it. type Inheritance = "None" | "All" | "Descendents" | "SelfAndChildren" | "Children"; interface Ace { principal: Principal; type: "allow" | "deny"; rights: string[]; objectType?: string; inheritedObjectType?: string; inheritance: Inheritance; inherited: boolean } interface NewAce { principal: string; type: "allow" | "deny"; rights: string[]; objectType?: string; inheritedObjectType?: string; inheritance?: Inheritance } // principal: "CONTOSO\Helpdesk" or a SID // showDeleted: see and restore objects in the Recycle Bin (CN=Deleted Objects). Restore = modify with it. interface Controls { showDeleted?: true } // Calls may run in parallel with Promise.all. Each run may make at most 200 calls.

Examples: // Enabled users in an OU, by name const r = await ad.search({ base: "OU=Sales,DC=contoso,DC=local", filter: "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))", attributes: ["sAMAccountName"] }); return r.entries.map(e => e.attributes.sAMAccountName[0]);

// How many members a group has, nested groups not expanded const g = await ad.get("CN=Helpdesk,OU=Groups,DC=contoso,DC=local", ["member"]); return g?.attributes.member?.length ?? 0;

// Disable a user (514 = normal account + disabled) return await ad.modify("CN=Jane Doe,OU=Sales,DC=contoso,DC=local", [{ op: "replace", attribute: "userAccountControl", values: "514" }]);

// Reset a password. unicodePwd takes the password in double quotes as UTF-16LE bytes. Add pwdLastSet "0" to force a change at next sign-in. const pw = '"' + newPassword + '"'; let b = ""; for (let i = 0; i < pw.length; i++) { const c = pw.charCodeAt(i); b += String.fromCharCode(c & 255, c >> 8); } return await ad.modify("CN=Jane Doe,OU=Sales,DC=contoso,DC=local", [{ op: "replace", attribute: "unicodePwd", values: { base64: btoa(b) } }, { op: "replace", attribute: "pwdLastSet", values: "0" }]);

// Restore a deleted user from the Recycle Bin to where it was. Delete isDeleted first, then set the new DN. const controls = { showDeleted: true }; const gone = (await ad.search({ base: "CN=Deleted Objects,DC=contoso,DC=local", filter: "(&(isDeleted=TRUE)(sAMAccountName=jdoe))", attributes: ["lastKnownParent", "msDS-LastKnownRDN"], controls })).entries[0]; const to = "CN=" + gone.attributes["msDS-LastKnownRDN"][0] + "," + gone.attributes.lastKnownParent[0]; return await ad.modify(gone.dn, [{ op: "delete", attribute: "isDeleted" }, { op: "replace", attribute: "distinguishedName", values: to }], { controls });

// Who can reset passwords in an OU: full control, all extended rights, or the Reset Password right const acl = await ad.getAcl("OU=Sales,DC=contoso,DC=local"); return acl.aces.filter(a => a.type === "allow" && (a.rights.includes("GenericAll") || (a.rights.includes("ExtendedRight") && (!a.objectType || a.objectType === "User-Force-Change-Password")))).map(a => a.principal.name ?? a.principal.sid);

// Password and lockout policy for the domain, from the Default Domain Policy's security settings const sa = (await gpo.get("Default Domain Policy")).securitySettings.systemAccess; return { minLength: sa.MinimumPasswordLength, lockoutAfter: sa.LockoutBadCount };

// Protect an OU from accidental deletion, as the admin tools do. AD allows a delete with Delete on the object or // DeleteChild on its parent, so deny both. To undo, removeAce the first; the parent's deny also protects its other children. await ad.addAce("OU=Sales,DC=contoso,DC=local", { principal: "S-1-1-0", type: "deny", rights: ["Delete", "DeleteTree"] }); return await ad.addAce("DC=contoso,DC=local", { principal: "S-1-1-0", type: "deny", rights: ["DeleteChild"] });

docsA

Search the Microsoft Learn documentation. Use it to answer how something in Active Directory works before or instead of reading a domain: Active Directory Domain Services, LDAP and its controls, the schema, Group Policy and its settings, Windows LAPS, delegation and permissions, and the Recycle Bin.

Returns the most relevant passages, each with its page title and link. To find an attribute or a policy setting, use search.

connections_listA

List the Active Directory domains this server can reach, with each one's mode. Use the alias or the domain name as the domain argument of execute.

connection_addA

Add an Active Directory domain by its DNS name. No sign-in: every call runs as the Windows user this server runs as, so that user's own permissions apply. A read connection refuses every add, modify, delete and move; choose write only when the person wants changes made. Adding an alias that exists replaces it.

connection_removeB

Remove a stored domain connection. Nothing in the domain changes.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.9/5.0

Scored across 6 tools

Disambiguation4/5

The tools split cleanly into connection management (connection_add/remove/list), informational lookups (docs, search), and domain operations (execute). The main risk is mild confusion between docs (Microsoft Learn) and search (domain schema catalogue), but their descriptions clearly delimit each to a different information source, so an agent can pick correctly.

Naming Consistency3/5

There is a mix: bare verbs (docs, execute, search) alongside noun_verb-prefixed names (connection_add, connection_remove, connections_list). The connection trio is also internally inconsistent in plurality (connection_ vs connections_), so conventions are readable but not uniform.

Tool Count4/5

Six tools is a lean, well-scoped set for an AD management server, with the heavy lifting consolidated into the execute mega-tool. It is slightly thin, but each tool has a distinct role, so nothing feels redundant.

Completeness4/5

Coverage is broad: schema/docs discovery, full CRUD and move via execute, ACL read/write (getAcl, addAce, removeAce), and a thorough GPO lifecycle (list, get, create, delete, link, unlink, set, remove, backup). Minor gaps remain, such as GPO restore and write access to security-template settings, but core admin workflows are covered.

Maintenance

ActivityNo data
ResponsivenessResponsive