@chapterai/mcp 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +50 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -22749,20 +22749,24 @@ function registerFileTools(server2) {
22749
22749
  );
22750
22750
  server2.tool(
22751
22751
  "read_file",
22752
- "Read the content of a file in a workspace",
22752
+ "Read the content of a file in a workspace. Use offset/limit to read a slice of a large file instead of the whole thing.",
22753
22753
  {
22754
22754
  workspaceId: external_exports.string().uuid().optional().describe("Project ID (auto-resolved from workspace link if omitted)"),
22755
22755
  workspaceName: external_exports.string().optional().describe("Project name (alternative to workspaceId)"),
22756
22756
  path: external_exports.string().describe("File path to read"),
22757
- ref: external_exports.string().optional().describe("Git ref (branch, tag, or commit SHA)")
22757
+ ref: external_exports.string().optional().describe("Git ref (branch, tag, or commit SHA)"),
22758
+ offset: external_exports.number().int().min(1).optional().describe("1-indexed line number to start reading from (omit to read from line 1)"),
22759
+ limit: external_exports.number().int().min(1).optional().describe("Maximum number of lines to return (omit to read to end of file)")
22758
22760
  },
22759
- async ({ workspaceId, workspaceName, path: path3, ref }) => {
22761
+ async ({ workspaceId, workspaceName, path: path3, ref, offset, limit }) => {
22760
22762
  try {
22761
22763
  const id = await resolveWorkspaceId(workspaceId, workspaceName);
22762
22764
  const result = await api.workspaces.readFile({
22763
22765
  workspaceId: id,
22764
22766
  path: path3,
22765
- ref
22767
+ ref,
22768
+ offset,
22769
+ limit
22766
22770
  });
22767
22771
  return success(result.content);
22768
22772
  } catch (error2) {
@@ -23563,6 +23567,15 @@ ${lines.join("\n")}`);
23563
23567
  }
23564
23568
 
23565
23569
  // src/tools/intelligence.ts
23570
+ function renderSnippet(snippet) {
23571
+ if (!snippet) return "";
23572
+ const padWidth = Math.max(4, String(snippet.endLine).length);
23573
+ const body = snippet.lines.map((line, i) => `${String(snippet.startLine + i).padStart(padWidth, " ")} | ${line}`).join("\n");
23574
+ return `
23575
+ \`\`\`
23576
+ ${body}
23577
+ \`\`\``;
23578
+ }
23566
23579
  function registerIntelligenceTools(server2) {
23567
23580
  server2.tool(
23568
23581
  "scan_codebase",
@@ -23604,48 +23617,49 @@ function registerIntelligenceTools(server2) {
23604
23617
  );
23605
23618
  server2.tool(
23606
23619
  "query_codebase",
23607
- "Search the codebase index for files, symbols, routes, endpoints, and copy matching a query. Returns ranked results with file paths and line numbers. The index must exist (run scan_codebase first).",
23620
+ "Search the codebase index for files, symbols, routes, endpoints, and copy matching a query. Returns ranked results with file paths, line numbers, and inline source snippets so you usually don't need a follow-up read_file call.",
23608
23621
  {
23609
23622
  workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
23610
23623
  workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
23611
23624
  query: external_exports.string().describe('Search query \u2014 e.g. "task creation", "auth middleware", "welcome page"'),
23612
- limit: external_exports.number().optional().describe("Max results per category (default 20)")
23625
+ limit: external_exports.number().optional().describe("Max results per category (default 20)"),
23626
+ contextLines: external_exports.number().int().min(0).max(20).default(6).describe("Lines of source to include around each hit. 0 disables snippets.")
23613
23627
  },
23614
- async ({ workspaceId, workspaceName, query, limit }) => {
23628
+ async ({ workspaceId, workspaceName, query, limit, contextLines }) => {
23615
23629
  try {
23616
23630
  const id = await resolveWorkspaceId(workspaceId, workspaceName);
23617
- const result = await api.intelligence.query({ workspaceId: id, query, limit });
23631
+ const result = await api.intelligence.query({ workspaceId: id, query, limit, contextLines });
23618
23632
  const sections = [];
23619
23633
  if (result.files.length > 0) {
23620
23634
  sections.push("## Files");
23621
23635
  for (const f of result.files) {
23622
23636
  const exports = f.exports.length > 0 ? ` (exports: ${f.exports.slice(0, 3).join(", ")})` : "";
23623
- sections.push(`- \`${f.path}\` [${f.category}]${exports}`);
23637
+ sections.push(`- \`${f.path}\` [${f.category}]${exports}${renderSnippet(f.snippet)}`);
23624
23638
  }
23625
23639
  }
23626
23640
  if (result.symbols.length > 0) {
23627
23641
  sections.push("\n## Symbols");
23628
23642
  for (const s of result.symbols) {
23629
23643
  const sig = s.signature ? ` \u2014 \`${s.signature}\`` : "";
23630
- sections.push(`- \`${s.name}\` (${s.kind}) at \`${s.filePath}:${s.line}\`${sig}`);
23644
+ sections.push(`- \`${s.name}\` (${s.kind}) at \`${s.filePath}:${s.line}\`${sig}${renderSnippet(s.snippet)}`);
23631
23645
  }
23632
23646
  }
23633
23647
  if (result.copy.length > 0) {
23634
23648
  sections.push("\n## Copy/Text");
23635
23649
  for (const c of result.copy) {
23636
- sections.push(`- "${c.text}" at \`${c.filePath}:${c.line}\` in ${c.context}`);
23650
+ sections.push(`- "${c.text}" at \`${c.filePath}:${c.line}\` in ${c.context}${renderSnippet(c.snippet)}`);
23637
23651
  }
23638
23652
  }
23639
23653
  if (result.routes.length > 0) {
23640
23654
  sections.push("\n## Routes");
23641
23655
  for (const r of result.routes) {
23642
- sections.push(`- ${r.method ?? "ANY"} \`${r.path}\` \u2192 ${r.handler} at \`${r.filePath}:${r.line}\``);
23656
+ sections.push(`- ${r.method ?? "ANY"} \`${r.path}\` \u2192 ${r.handler} at \`${r.filePath}:${r.line}\`${renderSnippet(r.snippet)}`);
23643
23657
  }
23644
23658
  }
23645
23659
  if (result.endpoints.length > 0) {
23646
23660
  sections.push("\n## API Endpoints");
23647
23661
  for (const e of result.endpoints) {
23648
- sections.push(`- \`${e.router ? e.router + "." : ""}${e.name}\` (${e.type}) at \`${e.filePath}:${e.line}\``);
23662
+ sections.push(`- \`${e.router ? e.router + "." : ""}${e.name}\` (${e.type}) at \`${e.filePath}:${e.line}\`${renderSnippet(e.snippet)}`);
23649
23663
  }
23650
23664
  }
23651
23665
  if (sections.length === 0) {
@@ -23659,24 +23673,30 @@ function registerIntelligenceTools(server2) {
23659
23673
  );
23660
23674
  server2.tool(
23661
23675
  "find_symbol",
23662
- "Find a specific function, class, component, type, or other symbol by name. Returns exact file path and line number.",
23676
+ "Find a specific function, class, component, type, or other symbol by name. Returns exact file path, line number, and an inline source snippet so the symbol body is right there.",
23663
23677
  {
23664
23678
  workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
23665
23679
  workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
23666
23680
  name: external_exports.string().describe('Symbol name to search for \u2014 e.g. "TaskCreateInline", "authorizeWorkspaceAccess"'),
23667
- kind: external_exports.string().optional().describe("Filter by kind: function, class, component, type, interface, enum, hook, struct, method")
23681
+ kind: external_exports.string().optional().describe("Filter by kind: function, class, component, type, interface, enum, hook, struct, method"),
23682
+ contextLines: external_exports.number().int().min(0).max(20).default(8).describe("Lines of source to include around each hit. 0 disables snippets.")
23668
23683
  },
23669
- async ({ workspaceId, workspaceName, name, kind }) => {
23684
+ async ({ workspaceId, workspaceName, name, kind, contextLines }) => {
23670
23685
  try {
23671
23686
  const id = await resolveWorkspaceId(workspaceId, workspaceName);
23672
- const results = await api.intelligence.findSymbol({ workspaceId: id, name, kind });
23687
+ const results = await api.intelligence.findSymbol({
23688
+ workspaceId: id,
23689
+ name,
23690
+ kind,
23691
+ contextLines
23692
+ });
23673
23693
  if (results.length === 0) {
23674
23694
  return success(`No symbol found matching "${name}"${kind ? ` (kind: ${kind})` : ""}`);
23675
23695
  }
23676
23696
  const lines = results.map((s) => {
23677
23697
  const sig = s.signature ? `
23678
23698
  ${s.signature}` : "";
23679
- return `- \`${s.name}\` (${s.kind}) at \`${s.filePath}:${s.line}\`${s.exported ? " [exported]" : ""}${sig}`;
23699
+ return `- \`${s.name}\` (${s.kind}) at \`${s.filePath}:${s.line}\`${s.exported ? " [exported]" : ""}${sig}${renderSnippet(s.snippet)}`;
23680
23700
  });
23681
23701
  return success(`Found ${results.length} match(es):
23682
23702
  ${lines.join("\n")}`);
@@ -23687,21 +23707,26 @@ ${lines.join("\n")}`);
23687
23707
  );
23688
23708
  server2.tool(
23689
23709
  "find_copy",
23690
- "Find user-facing text/copy in the codebase by content. Returns the file path, line number, and surrounding component context. Use this to quickly locate where a specific string appears in the UI.",
23710
+ "Find user-facing text/copy in the codebase by content. Returns the file path, line number, surrounding component context, and an inline JSX/template snippet.",
23691
23711
  {
23692
23712
  workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
23693
23713
  workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
23694
- text: external_exports.string().describe('Text to search for \u2014 e.g. "Welcome to Chapter", "Sign in", "No tasks yet"')
23714
+ text: external_exports.string().describe('Text to search for \u2014 e.g. "Welcome to Chapter", "Sign in", "No tasks yet"'),
23715
+ contextLines: external_exports.number().int().min(0).max(20).default(4).describe("Lines of source to include around each hit. 0 disables snippets.")
23695
23716
  },
23696
- async ({ workspaceId, workspaceName, text }) => {
23717
+ async ({ workspaceId, workspaceName, text, contextLines }) => {
23697
23718
  try {
23698
23719
  const id = await resolveWorkspaceId(workspaceId, workspaceName);
23699
- const results = await api.intelligence.findCopy({ workspaceId: id, text });
23720
+ const results = await api.intelligence.findCopy({
23721
+ workspaceId: id,
23722
+ text,
23723
+ contextLines
23724
+ });
23700
23725
  if (results.length === 0) {
23701
23726
  return success(`No copy found matching "${text}"`);
23702
23727
  }
23703
23728
  const lines = results.map(
23704
- (c) => `- "${c.text}" at \`${c.filePath}:${c.line}\` in \`${c.context}\` (${c.type})`
23729
+ (c) => `- "${c.text}" at \`${c.filePath}:${c.line}\` in \`${c.context}\` (${c.type})${renderSnippet(c.snippet)}`
23705
23730
  );
23706
23731
  return success(`Found ${results.length} match(es):
23707
23732
  ${lines.join("\n")}`);
@@ -23712,11 +23737,11 @@ ${lines.join("\n")}`);
23712
23737
  );
23713
23738
  server2.tool(
23714
23739
  "get_codebase_context",
23715
- "Get relevant source code and structure for a task. Returns matching files, symbols, endpoints, AND the actual source code of the top matching files. Use the returned content to answer questions or start work directly \u2014 only read additional files if you need more detail on a specific aspect, but do not repeat the search.",
23740
+ "Ask a question about the codebase or describe a task \u2014 returns an AI-synthesized answer with specific file paths, function names, and implementation details. The answer is ready to use: start working immediately based on it. Only read files if you need to see exact code not covered in the answer.",
23716
23741
  {
23717
23742
  workspaceId: external_exports.string().uuid().optional().describe("Workspace ID"),
23718
23743
  workspaceName: external_exports.string().optional().describe("Workspace name (alternative to ID)"),
23719
- taskDescription: external_exports.string().describe('Description of the task \u2014 e.g. "change the welcome message on the home page"')
23744
+ taskDescription: external_exports.string().describe('Question or task description \u2014 e.g. "how does the permission system work?" or "change the welcome message on the home page"')
23720
23745
  },
23721
23746
  async ({ workspaceId, workspaceName, taskDescription }) => {
23722
23747
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chapterai/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Chapter MCP server — gives AI agents access to Chapter projects, files, changes, and history",
5
5
  "type": "module",
6
6
  "bin": {