Documentation
¶
Index ¶
- Variables
- func FilterDate(r orgmcp.Render, dateFilter *DateFilter) (match bool, err error)
- func GetDiffOnly(of orgmcp.OrgFile, filePath string) (res string, err error)
- type BulletInput
- type BulletValue
- type ColumnList
- type DateFilter
- type HeaderInput
- type HeaderValue
- type StatusInputSchema
- type TextInputSchema
- type TextInputValue
- type VectorSearchInput
- type ViewInput
- type ViewItem
Constants ¶
This section is empty.
Variables ¶
View Source
var BulletTool = mcp.GenericTool[BulletInput]{
Name: "manage_bullet",
Description: "Add, remove or complete bullet points.\n" +
"The method parameter defines the action to take: 'add', 'remove', 'complete', 'toggle' or 'set_content'.\n" +
"- 'add': Adds a new bullet point at the specified index under the given header_uid. Requires 'content' and 'checkbox' parameters.\n" +
"- 'remove': Removes the bullet point identified by its uid.\n" +
"- 'complete': Marks the bullet point as completed (Checked).\n" +
"- 'toggle': Toggles the checkbox status of the bullet point between Checked and Unchecked.\n" +
"- 'set_content': Updates the content of the bullet point. Requires 'content' parameter.\n\n" +
"The 'header_uid' parameter specifies the parent header under which the bullet point resides.\n" +
"The 'bullet_index' parameter specifies the position of the bullet point under the header (0-based index).\n\n" +
"When targeting a bullet, the uid is constructed as `header_uid + '.b' + bullet_index`.\n" +
"Bullets are hierarchical meaning that bullets can have sub-bullets. Sub-bullets will use parent_bullet_uid + '.b' + bullet_sub_index like header_uid.b0.b1\n" +
"The add method is special as it requires the header_uid to be passed directly without any bullet index. The index will be determined by the tool itself.",
Callback: bulletFunc,
}
View Source
var HeaderTool = mcp.GenericTool[HeaderInput]{ Name: "manage_header", Description: "Add; remove or update headers in an Org file.\n" + "The method parameter defines the action to take: 'add'; 'remove'; 'update'.\n" + "For any method you can use a depth parameter to specify how many levels of children to return.\n" + "- 'add': Adds a new header at the specified index under the given parent_uid (pass this in the uid field of the function). Requires 'content' parameter.\n" + "- 'remove': Removes the header identified by its uid.\n" + "- 'update': Updates the header's content; status; or tags. Requires 'content'; 'status'; or 'tags' parameters.\n\n" + "It is recommended to pass uid's as string to the function. While they will almost certainly be numbers; this is not guaranteed.", Callback: func(ctx context.Context, input HeaderInput, options mcp.FuncOptions) (resp []any, err error) { var path string if input.Path == "" { path = options.DefaultPath } else { path = input.Path } orgFile, err := mcp.LoadOrgFile(ctx, path) if err != nil { return } if len(input.Columns) == 0 { uidCol := orgmcp.ColUid contentCol := orgmcp.ColContent input.Columns = []*orgmcp.Column{&uidCol, &contentCol} } var ordered []orgmcp.Render results := map[orgmcp.Uid]orgmcp.Render{} for _, headerOp := range input.Headers { switch headerOp.Method { case "add": parent, ok := orgFile.GetUid(orgmcp.NewUid(headerOp.Uid)).Split() if !ok { err = errors.New("invalid parent UID for adding header") return } var tags option.Option[orgmcp.TagList] if len(headerOp.Tags) == 0 { tags = option.None[orgmcp.TagList]() } else { tags = option.Some(orgmcp.TagList(headerOp.Tags)) } newHeader := orgmcp.NewHeader( orgmcp.HeaderStatus(headerOp.Status), headerOp.Content, ) newHeader.Tags = tags newHeader.SetLevel(parent.Level() + 1) parent.AddChildren(&newHeader) depth := 1 if headerOp.Depth != nil { depth = *headerOp.Depth } results[newHeader.Uid()] = &newHeader for _, child := range newHeader.ChildrenRec(depth) { results[child.Uid()] = child } case "remove": header, ok := orgFile.GetUid(orgmcp.NewUid(headerOp.Uid)).Split() if !ok { err = errors.New("invalid header UID for removal") return } parent, ok := orgFile.GetUid(header.ParentUid()).Split() if !ok { err = errors.New("Missing or invalid parent for header removal") return } err = parent.RemoveChildren(orgmcp.NewUid(headerOp.Uid)) if err != nil { return } depth := 1 if headerOp.Depth != nil { depth = *headerOp.Depth } results[header.Uid()] = header for _, child := range header.ChildrenRec(depth) { results[child.Uid()] = child } case "update": header, ok := option.Cast[orgmcp.Render, *orgmcp.Header](orgFile.GetUid(orgmcp.NewUid(headerOp.Uid))).Split() if !ok { err = errors.New("invalid header UID for update or not a header") return } if headerOp.Content != "" { header.Content = headerOp.Content } if headerOp.Status != "" { header.SetStatus(orgmcp.StatusFromString(headerOp.Status)) } if len(headerOp.Tags) != 0 { tags := option.Some(orgmcp.TagList(headerOp.Tags)) header.Tags = tags } depth := 1 if headerOp.Depth != nil { depth = *headerOp.Depth } results[header.Uid()] = header for _, child := range header.ChildrenRec(depth) { results[child.Uid()] = child } default: err = errors.New("invalid method for header management") } if err != nil { return } } locationTable := orgFile.BuildLocationTable() ordered = append(ordered, slices.Collect(maps.Values(results))...) slices.SortFunc(ordered, func(a, b orgmcp.Render) int { return (*locationTable)[a.Uid()] - (*locationTable)[b.Uid()] }) resp = append(resp, orgmcp.PrintCsv(ordered, input.Columns)) diff, err := mcp.WriteOrgFileToDisk(ctx, orgFile, path) if input.ShowDiff { resp = append(resp, diff) } return }, }
View Source
var StatusTool = mcp.GenericTool[StatusInputSchema]{ Name: "status_overview", Description: ` Provides an overview of task statuses in the Org file. It counts the number of tasks in each status category (TODO, NEXT, PROG, REVW, DONE, DELG). You will also receive all the uid's of the headers for each category in a list. This tool will also return an overview of all tags used in the Org file and the count of how many times each tag is used. Remember however that tags are recursive children will inherit the tags of their parents, so a header with the tag "project" and a child header without any tags will still be counted as having the "project" tag. `, Callback: func(ctx context.Context, input StatusInputSchema, options mcp.FuncOptions) (resp []any, err error) { var path string if input.Path == "" { path = options.DefaultPath } else { path = input.Path } orgFile, err := mcp.LoadOrgFile(ctx, path) if err != nil { return } resp = []any{map[string]any{ "status_overview": orgFile.GetStatusOverview(), "tag_overview": orgFile.GetTagOverview(), }} _, err = mcp.WriteOrgFileToDisk(ctx, orgFile, path) return }, }
View Source
var TextTool = mcp.GenericTool[TextInputSchema]{ Name: "manage_text", Description: ` Add, update or remove text content in an Org file. Plain text is a special type of content that cannot contain any nested elements. It is solely used for storing text content within a header or bullet point. Most of the time it will be more correct to use a bullet point to store text content as it allows for better organization and structuring of the content. But there are situations where plain text is more appropriate, such as large block of text without structure, like github issues etc. ## Methods ` + "`add`: Adds new text content to the specified parent element. The parent is passed via the uid parameter.\n" + "`update`: Updates the text content of the specified element. The element is identified by its uid.\n" + "`remove`: Removes the text content of the specified element. The element is identified by its uid.\n" + ` ## UID Constructions You can target either a header or a bullet point when adding text content. The uid will be the parent itself. Otherwise the uid construction is similar to the bullet tool. The text index is relative to the parent element, you can have multiple text elements under the same parent. The index is based on newline separation, but is deligneated by the parent element. ## Diff You always have the options with any modification to show a diff of the changes made. This can inform both you as well as the user about what exactly a tool call changed, and always you to undo changes if needed. ` + "`parent_uid + .t + text_index`\n", Callback: func(ctx context.Context, input TextInputSchema, options mcp.FuncOptions) (resp []any, err error) { var path string if input.Path == "" { path = options.DefaultPath } else { path = input.Path } orgFile, err := mcp.LoadOrgFile(ctx, path) if err != nil { return } affectedCount := 0 affectedItems := map[orgmcp.Uid]orgmcp.Render{} for _, mt := range input.Texts { var selected orgmcp.Render var ok bool if selected, ok = orgFile.GetUid(orgmcp.NewUid(mt.Uid)).Split(); !ok { resp = append(resp, fmt.Sprintf("Uid %s not found in %s", mt.Uid, path)) continue } switch mt.Method { case "add": if strings.Contains(mt.Content, "\n") { resp = append(resp, "Content for update method should not contain newlines. You should update each text element separately. As a fallback the newlines will be replaced with spaces.") mt.Content = strings.ReplaceAll(mt.Content, "\n", " ") } newPlainText := orgmcp.NewPlainText(mt.Content) selected.AddChildren(&newPlainText) case "update": if strings.Contains(mt.Content, "\n") { resp = append(resp, "Content for update method should not contain newlines. You should update each text element separately. As a fallback the newlines will be replaced with spaces.") mt.Content = strings.ReplaceAll(mt.Content, "\n", " ") } if plain, ok := selected.(*orgmcp.PlainText); ok { plain.SetContent(mt.Content) affectedItems[plain.Uid()] = plain affectedCount += 1 } else { resp = append(resp, fmt.Sprintf("Uid %s is not a plain text element, cannot update content", mt.Uid)) } continue case "remove": p_uid := selected.ParentUid() if parent, ok := orgFile.GetUid(p_uid).Split(); ok { parent.RemoveChildren(selected.Uid()) } else { resp = append(resp, fmt.Sprintf("Could not find parent for uid %s, skipping removal", mt.Uid)) } } affectedItems[selected.Uid()] = selected for _, child := range selected.ChildrenRec(-1) { affectedItems[child.Uid()] = child } affectedCount += 1 } ordered := []orgmcp.Render{} if input.ShowAffected == nil || *input.ShowAffected == true { locationTable := orgFile.BuildLocationTable() ordered = append(ordered, itertools.Collect(maps.Values(affectedItems))...) slices.SortFunc(ordered, func(a, b orgmcp.Render) int { return (*locationTable)[a.Uid()] - (*locationTable)[b.Uid()] }) resp = append(resp, orgmcp.PrintCsv(ordered, input.Columns)) resp = append(resp, map[string]any{ "affected_count": affectedCount, }) } diff, err := mcp.WriteOrgFileToDisk(ctx, orgFile, path) if input.ShowDiff { resp = append(resp, diff) } return }, }
View Source
var VectorSearch = mcp.GenericTool[VectorSearchInput]{ Name: "vector_search", Description: "Perform a vector search on all headers in the org file based on the provided query string. " + "Returns the top N most relevant headers.\n" + "It is optimal to include as much information as possible in the query, overflowing the text limit is hard. " + "So include context like timeframes, people involved, locations, etc. to get the best results.", Callback: func(ctx context.Context, input VectorSearchInput, options mcp.FuncOptions) (resp []any, err error) { filePath := options.DefaultPath if input.Path != "" { filePath = input.Path } if input.TopN <= 0 { input.TopN = 3.0 } of, err := mcp.LoadOrgFile(ctx, filePath) if err != nil { return } headers, err := of.VectorSearch(input.Query, input.TopN) resp = append(resp, slice.Map(headers, func(r orgmcp.Render) map[string]any { builder := strings.Builder{} r.Render(&builder, 1) return map[string]any{ "uid": r.Uid().String(), "content": builder.String(), "parent_uid": r.ParentUid().String(), } })) return }, }
View Source
var ViewTool = mcp.GenericTool[ViewInput]{ Name: "query_items", Description: ` # query_items View and filter Org items. ## Arguments - items: Array of filters (OR logic between items). - uid: string (optional) - status: "TODO" | "DONE" | "CHECKED" | ... - content: string (regex match) - tags: Array<string> (all tags must be present) - date_filter: - match: "SCHEDULED" | "DEADLINE" | "CLOSED" - range: number (days, negative for past) - show_closed: boolean - depth: number (optional, defaults to 1), determines how many levels of children to include in the CSV. - path: string (defaults to ./.tasks.org), unless you encounter errors about file not found or otherwise specified leave this empty. - columns: Array of column names to include in the output CSV. Defaults to [UID ; PREVIEW]. See the columns section below for available columns. ## Summary Returns a CSV of matching items. See the columns section in the common instructions for what columns you can specify. `, Callback: func(ctx context.Context, input ViewInput, options mcp.FuncOptions) (resp []any, err error) { var path string if input.Path == "" { path = options.DefaultPath } else { path = input.Path } orgFile, err := mcp.LoadOrgFile(ctx, path) if err != nil { return } if len(input.Columns) == 0 { input.Columns = []*orgmcp.Column{&orgmcp.ColUidValue, &orgmcp.ColPreviewValue} } results := map[orgmcp.Uid]orgmcp.Render{} for _, item := range input.Items { depth := 1 if item.Depth != nil { depth = *item.Depth } for _, render := range orgFile.ChildrenRec(-1) { if item.Uid != "" && render.Uid().String() != item.Uid { continue } if item.Status != nil && render.Status() != *item.Status { continue } if item.Content != "" { reg, err := regexp.Compile(item.Content) if err != nil { return nil, err } preview := render.Preview(-1) if !reg.MatchString(preview) { continue } } if item.Date != nil { match, err := FilterDate(render, item.Date) if err != nil { return nil, err } if !match { continue } } if len(item.Tags) > 0 { foundAll := true for _, tag := range item.Tags { if !slices.Contains(render.TagList(), tag) { foundAll = false break } } if !foundAll { continue } } results[render.Uid()] = render for _, child := range render.ChildrenRec(depth) { results[child.Uid()] = child } } } locationTable := orgFile.GetLocationTable() ordered := slices.Collect(maps.Values(results)) slices.SortFunc(ordered, func(a, b orgmcp.Render) int { return (*locationTable)[a.Uid()] - (*locationTable)[b.Uid()] }) resp = append(resp, orgmcp.PrintCsv(ordered, input.Columns)) _, err = mcp.WriteOrgFileToDisk(ctx, orgFile, path) return }, }
Functions ¶
func FilterDate ¶
func FilterDate(r orgmcp.Render, dateFilter *DateFilter) (match bool, err error)
Types ¶
type BulletInput ¶
type BulletInput struct {
Bullets []BulletValue `json:"bullets,omitempty" jsonschema:"description=List of bullet point operations to perform."`
Path string `json:"path,omitempty" jsonschema:"description=Optional file path; defaults to ./.tasks.org."`
ShowDiff bool `json:"show_diff,omitempty" jsonschema:"description=Whether to show the diff of changes made to the Org file."`
ShowAffected *bool `` /* 197-byte string literal not displayed */
Columns []*orgmcp.Column `` /* 137-byte string literal not displayed */
}
type BulletValue ¶
type BulletValue struct {
Uid string `` /* 152-byte string literal not displayed */
Method string `` /* 139-byte string literal not displayed */
Content string `json:"content,omitempty" jsonschema:"description=Text content of the bullet."`
Checkbox string `json:"checkbox,omitempty" jsonschema:"description=Checkbox status for the new bullet.,enum=None;Unchecked;Checked"`
}
type ColumnList ¶
func (ColumnList) GetSchema ¶
func (cl ColumnList) GetSchema() map[string]any
type DateFilter ¶
type DateFilter struct {
Match string `` /* 127-byte string literal not displayed */
ShowClosed bool `` /* 186-byte string literal not displayed */
Date *string `json:"date,omitempty" jsonschema:"description=The date to match against in YYYY-MM-DD format. Will default to today."`
Range *int `` /* 468-byte string literal not displayed */
}
type HeaderInput ¶
type HeaderInput struct {
Headers []HeaderValue `` /* 132-byte string literal not displayed */
Path string `` /* 224-byte string literal not displayed */
ShowDiff bool `` /* 170-byte string literal not displayed */
Columns []*orgmcp.Column `` /* 137-byte string literal not displayed */
}
type HeaderValue ¶
type HeaderValue struct {
Uid string `json:"uid" jsonschema:"description=UID of the header to modify or the parent_uid when adding."`
Method string `json:"method" jsonschema:"description=The method by which to manage the header.;enum=add;remove;update"`
Status string `` /* 259-byte string literal not displayed */
Content string `` /* 129-byte string literal not displayed */
Tags []string `` /* 208-byte string literal not displayed */
Depth *int `` /* 172-byte string literal not displayed */
}
type StatusInputSchema ¶
type StatusInputSchema struct {
Path string `` /* 224-byte string literal not displayed */
}
type TextInputSchema ¶
type TextInputSchema struct {
Texts []TextInputValue `json:"texts" jsonschema:"description=The list of text modifications to perform"`
Path string `` /* 159-byte string literal not displayed */
ShowDiff bool `` /* 127-byte string literal not displayed */
ShowAffected *bool `` /* 212-byte string literal not displayed */
Columns []*orgmcp.Column `` /* 137-byte string literal not displayed */
}
type TextInputValue ¶
type VectorSearchInput ¶
type VectorSearchInput struct {
Query string `json:"query" jsonschema:"description=The search query string.,required=true"`
TopN int `json:"top_n,omitempty" jsonschema:"description=The number of top relevant headers to return."`
Path string `json:"path,omitempty" jsonschema:"description=An optional file path; will default to the configured org file. (./.tasks.org)"`
}
type ViewInput ¶
type ViewInput struct {
Items []ViewItem `json:"items" jsonschema:"description=List of items to view based on their UIDs and filters.,required=true"`
Columns ColumnList `json:"columns,omitempty"`
Path string `json:"path,omitempty" jsonschema:"description=An optional file path; will default to ./.tasks.org"`
}
type ViewItem ¶
type ViewItem struct {
Uid string `json:"uid,omitempty" jsonschema:"description=UID of the header to view. If not provided, all headers are considered."`
Status *orgmcp.RenderStatus `` /* 183-byte string literal not displayed */
Content string `` /* 212-byte string literal not displayed */
Tags []string `` /* 131-byte string literal not displayed */
Depth *int `json:"depth,omitempty" jsonschema:"description=Depth of child headers to include. Default is 1 (only direct children)."`
Date *DateFilter `` /* 127-byte string literal not displayed */
}
Source Files
¶
Click to show internal directories.
Click to hide internal directories.